Android notification every 15 minutes crashes if app is closed - java

So i'm a complete newbie in Android Studio and I'm trying to make a simple app which does the following:
The app has a string array set on the /res/values folder called "frases".
The Main activity contains a button, when it's pressed, it increments a variable that represents the index of the array to be used (i've called it "iterador") and sets a textView to the value of frases[iterador].
Then on the onCreate method of the MainActivity, I have an alarnm with setRepeating that calls a class to receive a notification each 15 minutes. The purpose of this is to both have a textview in the MainActivity and a notification that can show the value of frases[iterador].
The problem here is whenever my app is closed (not stopped from the android settings menu, just closed) I get an error saying string array is null. And even worse, after the first notification, sometimes the second one won't appear even after 15 minutes if I don't open the app in my phone (even if it isn't closed).
I've tried to fix this by making both variables static so that I can access them from the notification class, but still doesn't work.
Is there a way to fix this and make the notificactions appear even whith the app closed?
My MainActivity vars and onCreate method:
public class MainActivity extends Activity implements View.OnClickListener {
//MY CODE
public static boolean primera; //APP OPENED FOR THE FIRST TIME
private Button b_corazon, b_carta;
private TextView t_frase;
public static final String PREFS_NAME = "MyPrefsFile";
//-------------------------STRING SHOW VARIABLES----------------------
public static String[] frases; //STRING ARRAY
public static int iterador; // INDEX FOR THE ARRAY
AlarmManager alarmManager; //ALARM MANAGER
#Override
protected void onCreate(Bundle savedInstanceState) {
Resources res=getResources(); //GET THE RESOURCES
frases=res.getStringArray(R.array.cartas); //ASIGN THE STRING ARRAY TO THE VARIABLE
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
//MI CODIGO
Intent intent = new Intent(getApplicationContext(), RecibeNotif.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),0,intent,0);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar firing = Calendar.getInstance(); //SCHEDULED TIME
Calendar current = Calendar.getInstance(); //CURRENT TIME
firing.set(Calendar.HOUR_OF_DAY,14);
firing.set(Calendar.MINUTE,20);
firing.set(Calendar.SECOND,0);
long intendedTime=firing.getTimeInMillis(); //SCHEDULED TIME
long currentTime=current.getTimeInMillis(); //CURRENT TIME
if(intendedTime>=currentTime){ //IF IT ISN'T THE TIME YET
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, intendedTime,AlarmManager.INTERVAL_FIFTEEN_MINUTES,pendingIntent);
}
else{ //IF THE TIME HAS ALREADY PASSED
firing.add(Calendar.MINUTE,15); //ADD 15 MINUTES FOR THE NEXT ALARM
intendedTime=firing.getTimeInMillis();
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, intendedTime,AlarmManager.INTERVAL_FIFTEEN_MINUTES,pendingIntent);
}
//SAVE THE VALUE OF ITERADOR IN THE APPLICATION DATA
SharedPreferences settings = getSharedPreferences(PREFS_NAME,0); //GUARDAR DATO DE ITERADOR
primera=getSharedPreferences("PREFERENCE",MODE_PRIVATE).getBoolean("primera", true);
iterador=settings.getInt("iterador",iterador);
init();
}
My BroadcastReceiver notification class:
public class RecibeNotif extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
String[] frasesaux;
int iteraux;
iteraux=MainActivity.iterador;
frasesaux=MainActivity.frases;
if(iteraux==frasesaux.length){
iteraux=0;
}
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent redir = new Intent(context,direct.class);
redir.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 100, redir, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.message) //ICONO DE LA NOTIFICACION
.setContentTitle(".////.")
.setContentText(frasesaux[iteraux])
.setAutoCancel(true);
notificationManager.notify(100,builder.build());
}
}
<-----------EDIT------------->
Okay, so it's fixed now, the notification works quite well. The only thing that still drives me crazy is how to update the value of iterador at the end of the notification class so it matches with iteraux.
So far, i've tried to edit the value of iterador using the SharedPreferences.editor but it doesn't seem to work.
Any ideas?
public void onReceive(Context context, Intent intent) {
String[] finale;
int iteraux;
SharedPreferences settings = context.getSharedPreferences(PREFS_NAME,0); //GUARDAR DATO DE ITERADOR
iteraux = settings.getInt("iterador",iterador);
Resources res= context.getResources(); //GET THE RESOURCES
finale=res.getStringArray(R.array.cartas); //ASIGN THE STRING ARRAY TO THE VARIABLE
//iteraux=MainActivity.iterador;
if(iteraux==finale.length){
iteraux=0;
}
else{
iteraux++;
}
//<-----------UPDATE ITERADOR VAR IN MAINACTIVITY
SharedPreferences.Editor editor2 = settings.edit();
editor2.putInt("iterador",iteraux);
editor2.commit();
// frasesaux=MainActivity.frases;

Your activity is not alive when you receive your notification so if you try to access the fields of Activity which is not alive , you get an exception
Solution
1.) use the context to get getStringArray(R.array.cartas)
2.) Use the same context to fetch data from SharedPreferences
public class RecibeNotif extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
String[] frasesaux;
int iteraux;
SharedPreferences settings = context.getSharedPreferences("MyPrefsFile",0);
iteraux = settings.getInt("iterador",0);
// ^^ your default value
//iteraux=MainActivity.iterador;
frasesaux=context.getResources().getStringArray(R.array.cartas);
// ^^ fetch the array
if(iteraux==frasesaux.length){
iteraux=0;
}
//...
}
}
Note : static won't make any difference because your array will only be initialized when onCreate will get called by the android OS so since this is not the case so hence your static fields will stay un-initialized mean null and 0.

Related

How to properly add new Activity onto top of the stack after tapping notification

I'm writing a really big app with plenty of activities. I have one class running in the background that is checking are there any changes on the server and perform appropriate action. One of the actions is to send notification to user about employee's clock in, out and similar. That kind of notification is clickable, and it should open employee's contact page PeopleSingleScene_Activity, and that is working as expected. However, when I click back button, application quits (no parent activities).
Code for creating notification with pending intent is as following:
public void sendEmployeeWorkNotification(String ticker, String title, String text, String loc, int employee_id) {
PendingIntent pendingIntent;
Intent intent;
Context currentContext = G.context;
if (loc.equals("LOC")) {
intent = new Intent(currentContext, PeopleSingleScene_Activity.class);
intent.putExtra("people_id", employee_id);
pendingIntent = PendingIntent.getActivity(currentContext, 0, intent, 0);
} else {
//some other actions
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(currentContext, CHANNEL_ID)
.setSmallIcon(R.drawable.vector_notif)
.setTicker(ticker)
.setContentTitle(title)
.setContentText(text)
.setColor(ContextCompat.getColor(currentContext, R.color.rcOutcome))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(text))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(currentContext);
notificationManager.notify(G.simpleMessageCounter, builder.build());
simpleSong(R.raw.notificationsimple);
G.simpleMessageCounter++;
}
G.context is the last activity context opened... It is a static variable in global class G. I'm creating it in a way:
public class SomeActivityClass extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.some_layout);
G.context = this;
....
}
....
}
I have also tried with context from my App class for retrieving string values or anything else with a context in classes without activities ...
public class App extends Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = this;
}
public static Context getContext(){
return mContext;
}
public static String getStr(int resid) {
return mContext.getResources().getString(resid);
}
}
When I use this context from App.getContext(); result is the same, after tapping back button, application quits.
Question is: Is there a problem with a context for creating pending intent and notification, or there is an issue with intent creation flags? I have tried lot of combinations with contexts and flags, but none of them works. And I cannot write (or I don't know maybe) some listeners on each activity to listen for this kind of event and open new intent from itself. There will be over 50 activities...
You have to make launch mode for this activity single top <activity android:launchMode="singleTop" />
Read more about Android Activity Launch Mode
Finally found a solution, thanks to my buddy Miljan.
When creating pendingIntent, just needed a proper flag. So instead of
pendingIntent = PendingIntent.getActivity(currentContext, 0, intent, 0);
just put a flag
pendingIntent = PendingIntent.getActivity(currentContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
and it works regularly. I can go back to previous activity after displaying activity intitiated by tapping notification.

Encapsulating alarm creation code in a different class than the Activity

I'm creating my first Android Notifications app, so I'm very much a beginner. I have a class, Notification.java, that asks the user for the time and date. Using these data, it creates an alarm that is triggered at the specified date and time.
Here is my code for Notification.java
public class Notification extends Activity {
private PendingIntent pendingIntent;
private SetAlarm alarm;
private Date date;
private Time time;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm);
findViewById(R.id.setTime).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setAlarmTime();
}
});
findViewById(R.id.setDate).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setAlarmDate();
}
});
findViewById(R.id.checkBox).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
createAlarm();
}
});
}
private void setAlarmTime() {
}
private void setAlarmDate() {
}
private void createAlarm() {
alarm = new SetAlarm();
}
}
The createAlarm() method is supposed to actually create the alarm using the information that the user has provided (i.e. time and date). However, I understand that I need the following code block to create the alarm?
private void setTheAlarm() {
Intent alarmIntent = new Intent(SetAlarm.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(SetAlarm.this, 0, alarmIntent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
int interval;
/* Set the alarm to the date specified by user */
/* Repeating on every x minutes interval */
}
However, the Notification.java is where I am extending 'Activity'. It is also where I have the 'pendingIntent; code.
So essentially, how can I move the alarm creation code into a separate class when the code dealing with the Activity is in an entirely different class?
Thanks for the help. I hope my question is clear enough.
Not exactly clear if that is what you want, but if I understand you correct, you need the alarmManager inside an extra class to reach it from everywhere? You could make a static one like this:
public class MyAlarmManager{
private static AlarmManager mAlarmManager;
private static PendingIntent mPendingIntent;
//start alarm
public static void setAlarm(Context context, int alarmId, long alarmTime) {
if (mAlarmManager== null) {
mAlarmManager= (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
Intent startAlarmIntent = new Intent(context, YouReceiver.class);
if(mPendingIntent==null){
mPendingIntent = PendingIntent.getBroadcast(context, alarmId,
startAlarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
//check the version because of doze mode since MM
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
alarmTime, mPendingIntent);
} else {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mPendingIntent);
}
}
//stop alarm
public static void stopAlarm(Context context, int id) {
if (mAlarmManager == null) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
Intent stopAlarmIntent = new Intent(context, YourReceiver.class);
if(mPendingIntent==null){
mPendingIntent= PendingIntent.getBroadcast(context, id, stopAlarmIntent, 0);
}
mAlarmManager.cancel(mPendingIntent);
mPendingIntent.cancel();
}
}
Then you can call it like:
MyAlarmManager.setAlarm(this, id, interval);
and stop it:
MyAlarmManager.stopAlarm(this, id);
You can do this from every class by passing the context and the identical id . The alarm id must be the same as you passed by starting the alarm, otherwise it will not work. Notice that above MarshMallow, there are some changes for AlarmManager and it´s possible that it does not work in every case. If your app get´s killed or goes into idle mode, the alarm won´t be triggered in every circumstance. To handle doze mode, see this:https://developer.android.com/training/monitoring-device-state/doze-standby.html
And be aware of any third party app and battery managers, that could kill your app. Also, Huawei devices have their own battery management besides the doze mode.
If this is not what you wanted, come back. Can´t guarantee that there is no error because I have overseen something, it´s from scratch.

How to pass custom Serializable object to BroadcastReceiver via PendingIntent

I am trying to pass a custom Serialized object from my IntentService to a BroadcastReceiver using PendingIntent.
Here is my custom object:
Row.java
public class Row implements Serializable {
private String name;
private String address;
public Row(BluetoothDevice device) {
this.name = device.getName();
this.address = device.getAddress();
}
}
Here is my IntentService
MyIntentService.java
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
protected void onHandleIntent(Intent workIntent) {
AlarmManager alarmMgr;
PendingIntent alarmPendingIntent;
Intent alarmIntent;
// The object "RowsList" is passed from my MainActivity and is correctly received by my IntentService.
// NO PROBLEMS HERE
Row[] arrRows = (Row[])workIntent.getSerializableExtra("RowsList");
Log.i(TAG, "Inside Intent Service...");
int interval = 2;
try{
if(interval != 0) {
alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarmIntent = new Intent(this, AlarmReceiver.class);
alarmIntent.putExtra("IntentReason", "Reason"); // THIS GETS PASSED
alarmIntent.putExtra("RowsList", arrRows); // THIS DOES NOT GET PASSED
alarmPendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + (interval * 1000), alarmPendingIntent);
}
}
catch (Exception ignored){}
}
}
MainActivity.java
// NO PROBLEMS HERE
private Intent myIntent;
myIntent = new Intent(getApplicationContext(), MyIntentService.class);
Log.i(TAG, "Starting Intent Service...");
myIntent.putExtra("RowsList", arrRows);
getApplicationContext().startService(intentListenBT);
Here is my BroadcastReceiver:
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
Row[] arrRows;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
String str = intent.getStringExtra("IntentReason"); // RECEIVED AS "Reason"
arrRows = (Row[])intent.getSerializableExtra("RowsList"); // HERE IT IS null
}
}
The most annoying part is that this code was working previously on my Nexus 6P (Lollipop 6.0 API23). It stopped working once I updated the same phone to Android 7.0(Nougat). Is there anything that has changed in Nougat that is causing this problem?
NOTE:
I ran my code using an emulator on Nexus 6P with API 23 and it works fine.
Most likely, you are running into the same sort of problem that you see with custom Parcelable implementations. Paraphrasing myself from that blog post: basically, if a core OS process needs to modify the Intent extras, that process winds up trying to recreate your Serializable objects as part of setting up the extras Bundle for modification. That process does not have your class and so it gets a runtime exception.
The most annoying part is that this code was working previously on my Nexus 6P (Lollipop 6.0 API23).
The behavior will vary by Android version, by how you are using the PendingIntent, and possibly by firmware/ROM. Do not assume that your current implementation will be reliable on any Android version.
Your only option is to not put the Serializable directly in an Intent extra. Use something other than Serializable (e.g., a nested Bundle), convert the Serializable into a byte[], etc.
This sample app demonstrates the latter approach, applied to a Parcelable object. The same basic technique should work for Serializable. (hat tip to AyeVeeKay for the link in the comments).

Android - Call a function from outside an activity (through AlarmManager and Notification)

Ok, so I have a main activity called 'Main.java'. This main activity starts an AlarmManager which fires an intent leading to 'AlarmReceiver.java'.
This 'AlarmReceiver.java' then creates a notification which has two buttons on it. One of the buttons is a deletion button, and so when the user clicks on that button, another intent is fired, leading it to 'DelPair.java'.
In DelPair.java, I modify a table in a Database, but then I need the UI of Main.java to reflect this change. I have created two functions in Main.java called updateArrayFromDB() and updateUIFromArray() to do this for me:
updateArrayFromDB() will sync an ArrayList created in Main.java to a
certain table in the DB.
updateUIFromArray() will change the UI of
Main.java to represent the ArrayList that has just been changed.
The problem is that I cannot call these two functions from DelPair.java (they don't exist in that space). I have come across Serializables in trying to find an answer but I don't know enough to know if they apply here or exactly how to implement them across the AlarmManager and the NotificationManager.
How can I access these methods from DelPair.java?
In Main.java:
public void updateArrayFromDB(){
//... The code for this is long and irrelevant
}
public void updateUIFromArray(){
//... The code for this is long and irrelevant
}
private void SendNotification() {
Intent intent = new Intent(this, AlarmReceiver.class);
//...
PendingIntent sender = PendingIntent.getBroadcast(this, 2 , intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, 5000, notif_freq, sender);
}
In AlarmReceiver.java:
Intent delPairI = new Intent(context, DelPair.class);
PendingIntent delPairPI = PendingIntent.getService(context, 0, delPairI, PendingIntent.FLAG_UPDATE_CURRENT);
Notification noti;
noti = new Notification.Builder(context)
//...
.addAction(R.drawable.ic_delete_icon, "Delete the thing", delPairPI)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, noti);
and then in DelPair.java:
public class DelPair extends IntentService {
//...
#Override
protected void onHandleIntent(final Intent intent) {
//...
Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
getApplicationContext().sendBroadcast(it);
handler.post(new Runnable() {
#Override
public void run() {
//... here is where I update the database, which works perfectly
//now need to update the UI and array in Main.java
updateArrayFromDB(); //these lines
updateUIFromArray(); //obviously don't work
}
});
}
}
Why not use broadcasts ? in onHandleIntent just send a broadcast
Intent i = new Intent();
i.setAction(CUSTOM_INTENT);
//put relevant data in intent
getApplicationContext().sendBroadcast(i);
The broadcast receiver:
public class IncomingReceiver extends BroadcastReceiver {
private MainActivity act;
public IncomingReceiver(MainActivity main){
this.act = act;
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(CUSTOM_INTENT)) {
System.out.println("GOT THE INTENT");
// call the method on act
}
}
}
In your activity onResume - register new IncomingReceiver, onPause unregister
private IncomingReceiver receiver;
public void onCreate(Bundle bOs){
//other codes
receiver = new IncomingReceiver(this);
}
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction(CUSTOM_INTENT);
registerReceiver(receiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(receiver);
super.onPause();
}
Since you need to have an updated UI based on database changes, you can call updateArrayFromDB() and updateUIFromArray() in the onResume() method of your activity so the UI gets updated each time the user enters the activity.

2 buttons on widget - refresh and show activity

I have widget with 2 buttons button with id refresh and second button with id detailsInfo. First button should trigger widget update, second button to show detailed info (downloaded after widget refreshed).
This is weather widget. Refresh should trigger to download full weather data. Basic weather info should be displayed directly on widget, full data on details activity, launched on detailsInfo button click.
This is my code:
public class AppWidget extends AppWidgetProvider
{
public static String ACTION_DETAILS = "m.m.meteowidget.ACTION_DETAILS";
#Override
public void onReceive(Context context, Intent intent)
{
Log.i("onReceive",intent.getAction());
super.onReceive(context, intent);
}
#Override
public void onUpdate(Context ctxt, AppWidgetManager mgr, int[] appWidgetIds)
{
ComponentName me = new ComponentName(ctxt, AppWidget.class);
final RemoteViews updateViews = new RemoteViews(ctxt.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent(ctxt, AppWidget.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setOnClickPendingIntent(R.id.refresh, pi);
Intent intent2 = new Intent(ctxt, DetailsActivity.class);
intent2.setAction(ACTION_DETAILS);
PendingIntent di = PendingIntent.getActivity(ctxt, 0, intent2, 0);
updateViews.setOnClickPendingIntent(R.id.detailsInfo, di);
mgr.updateAppWidget(me, updateViews);
for (int i = 0; i < appWidgetIds.length; i++)
new WeatherInfo(updateViews,appWidgetIds[i],mgr).execute();
}
}
WeatherInfo is class that actually performs weather details download (it extends AsyncTask). As you can see, it gets my updateViews as constructor argument and then sets basic weather info displayed on my widget.
However, I have no idea how to display detailed info activity and pass detailed weather info to it. When I try to run my activity as shown above, my widget fails to load ("Problems loading widget"), without any exception that I can debug.
Any ideas what am I doing wrong?
[edit]
This seems to be (almost) ok:
Widget provider:
#Override
public void onReceive(Context context, Intent intent)
{
Log.i("onReceive",intent.getAction());
super.onReceive(context, intent);
}
#Override
public void onUpdate(Context ctxt, AppWidgetManager mgr, int[] appWidgetIds)
{
ComponentName me = new ComponentName(ctxt, AppWidget.class);
final RemoteViews updateViews = new RemoteViews(ctxt.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent(ctxt, AppWidget.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
updateViews.setOnClickPendingIntent(R.id.refresh, pi);
mgr.updateAppWidget(me, updateViews);
Intent intent2 = new Intent(ctxt, DetailsActivity.class);
intent2.setAction(ACTION_DETAILS);
PendingIntent di = PendingIntent.getActivity(ctxt, 0, intent2, 0);
updateViews.setOnClickPendingIntent(R.id.detailsInfo, di);
mgr.updateAppWidget(me, updateViews);
for (int i = 0; i < appWidgetIds.length; i++)
new WeatherInfo(updateViews,appWidgetIds[i],mgr).execute();
}
My async task:
public class WeatherInfo extends AsyncTask<String, Void, Map>
{
private RemoteViews views;
private int WidgetID;
private AppWidgetManager WidgetManager;
private DetailsActivity detailsActivity;
public WeatherInfo(RemoteViews views, int appWidgetID, AppWidgetManager appWidgetManager)
{
this.views = views;
this.WidgetID = appWidgetID;
this.WidgetManager = appWidgetManager;
}
#Override
protected Map doInBackground(String... strings)
{
Document doc = null;
try
{
doc = Jsoup.connect("http://meteo.uwb.edu.pl/").get();
}
catch (IOException e)
{
Log.e("","Connection failed: " + e.getMessage());
return null;
}
Elements tables = doc.select("td");
Elements headers = tables.get(2).select("b");
Elements vals = tables.get(3).select("b");
Map all = new LinkedHashMap();
for (int i=0;i<headers.size() ; i++)
all.put(headers.get(i).text(),vals.get(i).text());
Global.weatherInfo = all;
return all;
}
#Override
protected void onPostExecute(Map map)
{
if(map==null) return;
String txt = "";
String temp = (String) map.values().toArray()[0];
String hum = (String) map.values().toArray()[1];
String pressure = (String) map.values().toArray()[2];
String temp2 = "Odczuwalna: " + map.values().toArray()[3];
views.setTextViewText(R.id.info_temp, temp);
views.setTextViewText(R.id.info_temp2, temp2);
views.setTextViewText(R.id.info_hum, hum);
views.setTextViewText(R.id.info_pressure, pressure);
WidgetManager.updateAppWidget(WidgetID, views);
}
}
So I there is Global class with weatherInfo static field to share value between my thread and details activity.
However, there are 2 things that I have no idea how to fix:
- if activity is destroyed (removed from last app list in Android), after I press details button on my widget, activity is empty (bacause Global.weather info is null). I need to trigger widget refresh again and then lanunch my activity
- if I try to set Global.weatherInfo inside PostExecute method my widgets fails to show, without any exception thrown - why?
- I also tried to trigger my async task on create my activity. So i created second WeatherInfo constructor and passed DetailSActivity object into this, to be able to refresh my activity. Even if I don't use that second constructor, my widgets again fails to load without any exception.
I'm confused, can anybody tell me what's going on here? And how to solve my problem?
Create AsyncTask separately. Return the values from postexecute() method. Handler will give you better solution to handle response of asynktask.
Call this asynctask before populating the values in UI components. Handle error case separately either by dialogue box whatever you wish.
follow the link for clearance in handler
I made it working by storing data in sqlite database. So, after widget refresh, all data are saved to database. Database is also for data cache, so if there is no internet connection, widget disaplays cached data. The same cached data will be loaded to be displayed on my activity.

Categories