I’m an experienced AS3 developer and I’ve done quite some stuff with Java for my backends but I’m new to Native Android development so I’m having troubles with some basic Tasks for my first Project.
So hope one of you cracks can help me out here or point me in the right directions, it would be much appreciated and I’ll repay be helping out in the AS3 section. That briefly about me, since it’s my first post. ;)
The task at hand is to get the users postcode on application launch. I’ve been using an AsyncTask for the reverse geocoding and It generally seems to work. But only when I call the ReverseGeocodingTask on a button click, and give it a few seconds before I do so. If I press it immediately it sometimes works and sometimes doesn’t, so obviously when I call it in the onCreate method the app crashes aswell. It also crashes when I turn the internet off on the phone. I reckoned the network provider location should be sufficient and there is no need for the GPS accuracy and the additional permissions.
If the INet is turned off by the user, it should just show a message that the postcode can’t be found and give the user the option to input it manually.
I figured that the currentLocation to pass to the geocoding has not been found yet and is throwing a NullPointerException, so I tried to prevent that by checking it before the call. But that didn’t really help and is no solution for the final version anyways.
Since its always best to show the code so u guys know what’s going on, here goes:
package com.adix.DroidTest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.location.*;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import static java.util.Locale.getDefault;
public class MyActivity extends Activity implements View.OnClickListener {
Button getPostCode, confirm;
TextView tvPostcode;
LocationManager locationManager;
Location currentLocation;
double currentLatitude;
double currentLongitude;
private Handler mHandler;
private static final int UPDATE_ADDRESS = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
AtomicReference<LocationListener> locationListener = new AtomicReference<LocationListener>(new LocationListener() {
public void onLocationChanged(Location location) {
updateLocation(location);
}
private void updateLocation(Location location) {
currentLocation = location;
currentLatitude = currentLocation.getLatitude();
currentLongitude = currentLocation.getLongitude();
}
public void onStatusChanged(
String provider, int status, Bundle extras) {
}
public void onProviderEnabled(String provider) {
}
public void onProviderDisabled(String provider) {
}
});
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener.get());
//getAddress();
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_ADDRESS:
tvPostcode.setText((String) msg.obj);
break;
}
}
};
}
private void init() {
getPostCode = (Button)findViewById(R.id.bGetPostCode);
confirm = (Button)findViewById(R.id.bConfirm);
tvPostcode = (TextView)findViewById(R.id.tvPostcode);
getPostCode.setOnClickListener(this);
confirm.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bGetPostCode:
currentLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(currentLocation != null) {
Log.d("TRACE",currentLocation.toString());
Toast.makeText(this, "Suche Postleitzahl", Toast.LENGTH_LONG).show();
(new ReverseGeocodingTask(this)).execute(new Location[]{currentLocation});
}
break;
case R.id.bConfirm:
Intent i = new Intent(MyActivity.this, MainMenu.class);
startActivity(i);
finish();
}
}
private class ReverseGeocodingTask extends AsyncTask<Location, Void, Void> {
Context mContext;
public ReverseGeocodingTask(Context context) {
super();
mContext = context;
}
#Override
protected Void doInBackground(Location... locations) {
try{
Geocoder gcd = new Geocoder(mContext, Locale.getDefault());
List<Address> addresses = gcd.getFromLocation(currentLatitude, currentLongitude,100);
Address address = addresses.get(0);
StringBuilder result = new StringBuilder();
result.append(address.getPostalCode());
// tvPostcode.setText(result.toString());
Message.obtain(mHandler, UPDATE_ADDRESS, result.toString()).sendToTarget();
}
catch(IOException ex){
tvPostcode.setText(ex.getMessage().toString());
Message.obtain(mHandler, UPDATE_ADDRESS, ex.getMessage().toString()).sendToTarget();
}
return null;
}
}
}
I gave this a rest since this post to see if someone sees my mistake. Since I hadn't got an answer, I gave it another shot today. And fortunately found the answer quite quick in the end. Obviously I needed to execute the ReverseGeocodingTask in the onLocationChanged method after updateLocation.
Related
I'm working on an Android app, but am stuck at a problem I can't seem to find an answer to. I want to call the method "updateTime" in class "MainActivity" from method "run" (thus calling it from a Thread) in class "TaskHandler".
I've Googled for the answer for about an hour now, visited multiple websites and found multiple solutions, of which non worked for me. I have also asked about it in the LinusTechTips and Corsair discord servers.
MainActivity class:
package thedutchmc.net.alarm;
import android.os.Bundle;
import android.widget.EditText;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.time.LocalTime;
public class MainActivity extends AppCompatActivity {
public static String alarmTime;
public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
public static boolean alarmBool = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView mTextView = (TextView) findViewById(R.id.currentTime);
mTextView.setText("Current Time: ");
Main.scheduler.scheduleAtFixedRate(new TaskHandler(), 1, 1, TimeUnit.SECONDS);
}
public void onSubmit(View v){
System.out.println("Submit!");
EditText alarmTimeEditText = (EditText) findViewById(R.id.setAlarmTime);
alarmTime = alarmTimeEditText.getText().toString();
System.out.println("MainActivity (alarmTime): " + alarmTime);
alarmBool = true;
}
public void updateTime() {
TextView seeTime = (TextView) findViewById(R.id.currentTime);
seeTime.setText(LocalTime.now().toString());
}
}
TaskHandler class:
package thedutchmc.net.alarm;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class TaskHandler implements Runnable {
final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HH:mm");
public static boolean isRinging = false;
private String alarmTime;
public static final MainActivity activity = new MainActivity();
#Override
public void run() {
activity.updateTime();
if (checkAlarmBool()) {
System.out.println("Bool true! Alarm!");
Main.alarm.set(false);
Main.alarm.ringAlarm();
}
}
boolean checkAlarmBool() {
if (MainActivity.alarmBool && !isRinging) {
String lTime = LocalTime.now().format(dtf);
System.out.println("TaskHandler alarmTime: " + MainActivity.alarmTime);
System.out.println("TaskHandler LocalTime: " + lTime);
if(lTime.equalsIgnoreCase(MainActivity.alarmTime)) {
isRinging = true;
return true;
} else {
return false;
}
} else {
return false;
}
}
I hope someone can help me :)
Make TaskHandler an inner class inside MainActivity. Then you'll be able to call updateTime(). And drop that static final MainActivity variable, you won't need it if TaskHandler is inside MainActivity. Never create activities with the new operator.
One other thing you'll probably run into, you can't update UI from a background thread, so you'll probably want to use runOnUiThread(Runnable) either when calling updateTime() or inside updateTime().
You can't directly access an Activity's methods outside of the Activity itself. The recommended way to communicate among components is to use Broadcasts.
You do have direct access to your Activity, because you instantiate the TaskHandler from it. You could just pass your Activity in the constructor of TaskHandler (and save it as a global variable in the TaskHandler), but this might lead to crashes if that Activity is finished before TaskHandler executes.
Add a Context to TaskHandler's constructor:
private Context context;
public TaskHandler(Context context) {
this.context = context;
}
and instantiate the TaskHandler with
new TaskHandler(getApplicationContext());
and you'll be able to send a Broadcast that you can then receive in a BroadcastReceiver registered inside your Activity (read the link for details on BroadcastReceivers and such).
Replace Context with MainActivity, and getApplicationContext() with this, and you can just directly call the method you want, but that can cause crashes, and this will only work if TaskHandler is only used inside MainActivity.
If it is only used inside MainActivity, just make it an inner class, and then you can call the method directly, without any reference.
No matter what you do, you can't make a new instance of Activity classes yourself and expect them to work.
Use Broadcasts.
In Your TaskHandler class, from inside "run" method, send broadcast:
Intent i = new Intent("run_method");
sendBroadcast(i);
In Your MainActivity class, in onResume(), register Broadcast Receiver:
private BroadcastReceiver receiver;
if (receiver == null){
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateTime();
}
};
}
registerReceiver(receiver, new IntentFilter("run_method"));
I'm very new on Android world. After developing a beautiful Rest API I thought Android development will be easy, but I'm stuck on basics.
On my Android app, I created Login, that makes an API call, that return a token when valid credentials are provided; this token is stored on shared preferences, and user is redirected to the principal activity: HomeActivity.
This Activity has a lot of work to do:
It has a BottomNavigationBar, so when the user clicks on a button of it, a new Fragment will be loaded.
Call to the API endpoint to get resources and show it depending on the fragment.
Store API response on Database to avoid overload server.
Surely, for Android developer this will be quite easy, but for my is like this:
import android.arch.persistence.room.Room;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.ibosca.thub.database.AppDatabase;
import com.ibosca.thub.helpers.BottomNavigationViewHelper;
import com.ibosca.thub.models.Channel;
import com.ibosca.thub.models.Content;
import com.ibosca.thub.models.ContentList;
import com.ibosca.thub.models.Town;
import com.ibosca.thub.models.User;
import com.ibosca.thub.parser.ContentParser;
import com.ibosca.thub.volley.MySingleton;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HomeActivity extends AppCompatActivity {
private String userToken;
public TextView contentList;
private ContentParser contentParser = new ContentParser();
public static AppDatabase db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "townhub").build();
contentList = (TextView) findViewById(R.id.contentList);
loadContents();
SharedPreferences sharedPref = getSharedPreferences(MainActivity.PACKAGE_NAME, Context.MODE_PRIVATE);
userToken = sharedPref.getString("token", null);
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
View contentsButton = bottomNavigationView.findViewById(R.id.action_contents);
contentsButton.performClick();
bottomNavigationView.setOnNavigationItemSelectedListener(
new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Toast toast = Toast.makeText(getApplicationContext(), "UNDF", Toast.LENGTH_LONG);
switch (item.getItemId()) {
case R.id.action_towns:
toast = Toast.makeText(getApplicationContext(), "Towns", Toast.LENGTH_LONG);
break;
case R.id.action_channels:
toast = Toast.makeText(getApplicationContext(), "Channels", Toast.LENGTH_LONG);
break;
case R.id.action_contents:
loadContents();
break;
case R.id.action_question:
toast = Toast.makeText(getApplicationContext(), "Questions", Toast.LENGTH_LONG);
break;
case R.id.action_user:
toast = Toast.makeText(getApplicationContext(), "Settings", Toast.LENGTH_LONG);
break;
}
toast.show();
return true;
}
});
}
public void ExecuteInsert(ContentList...lists){
new InsertContents().execute(lists);
}
protected void loadContents() {
String url = MySingleton.BASE_URL + "/contents";
JsonObjectRequest jsObjRequest = new JsonObjectRequest
(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
ContentList list = contentParser.fromContents(response);
ContentList[] lists = new ContentList[1];
lists[0] = list;
ExecuteInsert(lists);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(), "Failed to connect", Toast.LENGTH_LONG).show();
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + userToken);
return headers;
}
};
MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
}
public static class InsertContents extends AsyncTask<ContentList, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
//Perform pre-adding operation here.
}
#Override
protected Void doInBackground(ContentList...lists) {
ContentList list = lists[0];
//Insert towns, channels
db.townDao().insertArrayList(list.getTowns());
db.channelDao().insertArrayList(list.getChannels());
db.userDao().insertArrayList(list.getUsers());
db.contentDao().insertArrayList(list.getContents());
//Select data from DB
List<Town> towns = db.townDao().getAll();
List<Channel> channels = db.channelDao().getAll();
List<User> users = db.userDao().getAll();
List<Content> contents = db.contentDao().getAll();
for (int i = 0; i < contents.size(); i++) {
Content content = contents.get(i);
Town contentTown = db.townDao().findById(content.getTownId());
Log.i("Poble: ", contentTown.getName());
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
//To after addition operation here.
}
}
}
For a quick summary, on method loadContents() I'm making the API call; and the class InsertContents it's where I play with local database.
Finally, the questions:
1) As you can see, I'm using Volley to make API calls. Are there any best practice to put any "api endpoint" on a separated class, and use this class from the Activity? How to separate this code on Android development?
2) Same for Database management. How can I put the code on a separated class and call it from the Activity? This is currently accomplished, but... I'm unable to update my TextView from the AsyncTask (Update the TextView it's only a easy try, my final goal it's to use a ListView or ReciclerView.
Any suggestions for improvement are welcome.
You might try the Repository pattern.
The idea is more or less as follows, lets say you have a Car domain class and you database or api interactions perform tipical CRUD operation like, insert a car, retrieve a list of al card or one by its plate number.
You could create an interface like
public interface CarRepository {
void insertCar(#NonNull Car car);
List<Car> getAllCars();
Car getCarByPlate(#NonNull String plateNumber);
}
Then you can create concrete implementations of said interface depending of which source are you using for storing your data.
For example if using volley you could create a RestCarRepository that extends CarRepository and fetch/ store data from a rest api using Volley. Or a DBCarRepository that uses SQLite (or any other database engine).
Finally you can declare your repository in you activity so you abstract the logic of fetching data.
Disclaimer: There are lots of articles regarding repository pattern (as the posted at the beggining of the answer) and this answer could become more complex when adding more patterns as DI or MVP, this is so you have a grasp of the idea.
Short answer for both cases: It would be better separate the view (activity/fragment) from the model or data. You are mixing everything in the activity and in this small case could be ok but if your app grows will be dificult to read and understand and cause problems with the activity/fragment lifecycle.
There are a lot of different aproaches to separate concerns in android in order to do a cleaner code.
I recommend you this repo talking about clean architecture in android apps https://github.com/android10/Android-CleanArchitecture
Also Google has a relatively new library to implements several patterns
https://developer.android.com/topic/libraries/architecture/adding-components.html
Here you have some help about databases, paging, viewmodel, etc.
EDIT:
Answering more in detail:
1) You can follow the Model View Presenter (MVP) pattern considering the activity/fragment like only a View (a component with an only one responsibility, render components) and creating a class (the Presenter) who has the knowledge of the model/data (your api calls) and act like a bridge between View and Model.
The view will delegate in Presenter the calls to the model (for example if some button is pressed) and the Presenter will return the data to the View and the View will have only the way of paint the screen.
2) You can follow the same approach calling the Presenter in order to retrieve the information and painting the data in a RecyclerView.
You can use a ThreadPoolExecutor in order to decouple the data from the activity.
I am new to android and I have found similar question here but none of it could solve my problem. The code below is basically to get two double values(latitude and longitude) from Firebase and plot them on the map. There is a toast that is shown when the user has logged out i.e when the double values have been removed from the database. The problem here is that this toast is shown even after I go back to other activities. How can I stop this. I just want to end the Activity where I have put the finishes at.
Any help would be appreciated. Thanks in advance!
Edit: If I open many instances of this activity (View locations of many people) I would get the toast of every instance as each person logs out. I am concerned this would waste a lot of resources.
Could it be because of the getMapAsync? Have read that Async tasks run on a different thread and does not stop even after finish().
java code:
package com.example.android.managers;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
public class LocateS extends Activity implements OnMapReadyCallback {
MapView mapView;
GoogleMap googleMap;
String username;
int firstTime=0;
LocationDetails loc;
ChildEventListener listen = null;
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("Staff");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_locate);
Bundle extras = getIntent().getExtras();
username= extras.getString("user");
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
listen = myRef.child(username).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
loc = dataSnapshot.getValue(LocationDetails.class);
if(loc!=null)
setMap();
else
Toast.makeText(getApplicationContext(),"Location not received yet",Toast.LENGTH_LONG).show();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
loc = dataSnapshot.getValue(LocationDetails.class);
if(loc!=null)
setMap();
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Toast.makeText(getApplicationContext(), username+" logged out", Toast.LENGTH_SHORT).show();
myRef.removeEventListener(listen);
// android.os.Process.killProcess(android.os.Process.myPid());
finish();
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
public void setMap(){
if(googleMap!=null){
googleMap.clear();
LatLng coordinate = new LatLng(loc.getLatitude(),loc.getLongitude());
googleMap.addMarker(new MarkerOptions().position(coordinate));
if(firstTime == 0) {
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(coordinate, 17.0f);
googleMap.moveCamera(cameraUpdate);
firstTime++;
}
}
}
#Override
public void onMapReady(GoogleMap map) {
googleMap=map;
map.getUiSettings().setZoomControlsEnabled(true);
}
#Override
public void onResume() {
mapView.onResume();
super.onResume();
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onStop(){
finish();
super.onStop();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onLowMemory() {
mapView.onLowMemory();
super.onLowMemory();
}
#Override
public void onBackPressed() {
myRef.removeEventListener(listen);
finish();
// android.os.Process.killProcess(android.os.Process.myPid());
super.onBackPressed();
}
}
xml code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main2"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.managers.Locate">
<com.google.android.gms.maps.MapView
android:id="#+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</RelativeLayout>
add finish after in your onDestroy() method
finish();
Your issue is the listener was not removed when you moved to another activity except when you back pressed.
You should put the
myRef.removeEventListener(listen);
Into the onPause/onStop method.
As your toast is still alive and any change to the child, it will trigger the toast.
You can use ActivityName.this.finish();
cancel your toast before calling finish
final Toast testing = Toast.makeText(context, "start.", Toast.LENGTH_SHORT);
testing.show();
and use this when you r calling finish.
testing.cancel();
finish();
toast will display until is will not complete execution no matter activity is closed or not. toast will tack 3 second(sort duration) . make sure you r calling after 3 second or cancel toast and call finish any time
The toast is shown for the duration corresponding to Toast.LENGTH_SHORT which is 3.5 sec according to this, no matter the calling activity is active or not. I think what you want is to show the toast that user had logged out. Wait till it is shown on screen. As soon as it is finished showing, destroy activity. This may be achieved by calling show toast, waiting for 3.5 sec and then destroying the activity.
So, as described here,
Toast.makeText(getApplicationContext(), username+" logged out", Toast.LENGTH_SHORT).show();
Thread thread = new Thread(){
#Override
public void run() {
try {
Thread.sleep(3500);
LocateS.this.finish();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
might work.
Alternatively, if you want to wait for duration other than 3.5 sec or that corresponding to Toast.LENGTH_LONG, follow this.
Edit:
Sorry I misinterpreted your problem!
I think the problem might be that we cannot detach a listener from its own method. I think putting myRef.removeEventListener(listen); in onDestroy() instead might be helpful.
One of the functions in my app is sending email. The email list is generated by querying from SQLite database table. So sending email and query data from SQLite database at the same activity. It is not working. Sending email code works if I apply the code in a simple app. Query works. It is not working when I put them all together. After reading online, my feeling is that I need to create a new thread that handle the SQLite database query. I am very new for android and java and don't know how to create a new thread (background).
Could somebody help me? Many many thanks!!!!!
my activity code as following:
package jhapps.com.demographics;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class PromotionEmailMonthTop10 extends Activity {
private EditText subjectGroupTop10,bodyGroupTop10;
private Button btnMonthTop10;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_promotion_email_month_top10);
subjectGroupTop10=(EditText)findViewById(R.id.subjectMonthTop10);
bodyGroupTop10=(EditText)findViewById(R.id.bodyMonthTop10);
btnMonthTop10=(Button)findViewById(R.id.btnMonthTop10);
btnMonthTop10.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EmailMonthTop10();
// after sending the email, clear the fields
subjectGroupTop10.setText("");
bodyGroupTop10.setText("");
}
});
}
//get month top 10 email list
protected void EmailMonthTop10() {
DataBaseHelper dataBaseHelper=new DataBaseHelper(PromotionEmailMonthTop10.this);
String[] emailGroupTop10=new String[dataBaseHelper.eMailListMonthTop10().size()];
for(int i=0;i<dataBaseHelper.eMailListMonthTop10().size();i++){
emailGroupTop10[i]=dataBaseHelper.eMailListMonthTop10().get(i);
}
String subjects=subjectGroupTop10.getText().toString();
String bodytext=bodyGroupTop10.getText().toString();
//start email intent
Intent email = new Intent(Intent.ACTION_SENDTO);
// prompts email clients only
email.setType("message/rfc822");
email.setData(Uri.parse("mailto:"));
email.putExtra(Intent.EXTRA_EMAIL,emailGroupTop10 );
// email.putExtra(Intent.EXTRA_EMAIL,new String []{"junrudeng#gmail.com","huangji8#gmail.com"});
email.putExtra(Intent.EXTRA_SUBJECT, subjects);
email.putExtra(Intent.EXTRA_TEXT, bodytext);
try {
// the user can choose the email client
startActivity(Intent.createChooser(email, "Choose an email client from..."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(PromotionEmailMonthTop10.this, "No email client installed.",
Toast.LENGTH_LONG).show();
}
}
}
You should never execute database queries or network calls on the main thread. If you want to query a database to display data you probably want to you a AsyncTask for that.
Something like the following should work:
public class PromotionEmailMonthTop10 extends Activity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
btnMonthTop10.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new SendEmailTop10Task().execute();
}
});
}
class SendEmailTop10Task extends AsyncTask<Void, Void, Void> {
// This is called on a seperate thread
#Override
protected Void doInBackground(Void... voids) {
EmailMonthTop10();
}
// This is called on the main thread
#Override
protected void onPostExecute(Integer status) {
subjectGroupTop10.setText("");
bodyGroupTop10.setText("");
}
}
}
Please consider renaming your method taking the java naming conventions under consideration
I have been using this Android Guide
While it has been a pleasant experience so far, I am experiencing my first problem. I copied all the code from the source that is in the link, and pasted it to the project folder, replacing all old files. Before starting to understand what I had pasted, I thought it would be logical to run the code first to check for problems. The project wouldn't run because of an R object missing. After importing it (Eclipse's solution to the problem), more errors popped up. I tried searching for an answer, both on the Internet and in the book, but to no avail. Since my software is up to date, I doubt this is a problem on the software's side. And since the code is available online, I think the problem would have popped up and been fixed.
Thank you in advance for the help. For extra details please ask in the comments.
The code:
MainActivity.java
package com.dummies.android.silentmodetoggle;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity {
private AudioManager mAudioManager;
private boolean mPhoneIsSilent;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
checkIfPhoneIsSilent();
setButtonClickListener();
Log.d("SilentModeApp", "This is a test");
}
private void setButtonClickListener() {
Button toggleButton = (Button)findViewById(R.id.toggleButton);
toggleButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mPhoneIsSilent) {
// Change back to normal mode
mAudioManager
.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
mPhoneIsSilent = false;
} else {
// Change to silent mode
mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
mPhoneIsSilent = true;
}
// Now toggle the UI again
toggleUi();
}
});
}
/**
* Checks to see if the phone is currently in silent mode.
*/
private void checkIfPhoneIsSilent() {
int ringerMode = mAudioManager.getRingerMode();
if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
mPhoneIsSilent = true;
} else {
mPhoneIsSilent = false;
}
}
/**
* Toggles the UI images from silent
* to normal and vice versa.
*/
private void toggleUi() {
ImageView imageView =
(ImageView) findViewById(R.id.phone_icon);
Drawable newPhoneImage;
if (mPhoneIsSilent) {
newPhoneImage =
getResources().getDrawable(R.drawable.phone_silent);
} else {
newPhoneImage =
getResources().getDrawable(R.drawable.phone_on);
}
imageView.setImageDrawable(newPhoneImage);
}
#Override
protected void onResume() {
super.onResume();
checkIfPhoneIsSilent();
toggleUi();
};
}
Try cleaning your project, this will rebuild your R file. If there is still no R file in your file-tree then you may have an error in one your xml layout files. Eclipse may not tell you this so be vigilant and check through all the files in the /res folder. Also, never import R when this happens.
Did you check if there is a variable named action_settings in /res/values/string.xml if it does not exist please create one and then clean using projects -> clean makesure that build Automatically is checked