How to create an intent to an Activity but with updated content (not initial)?

Viewed 57

I'm new in Android SDK and develop an app which sends notifications every 30 minutes using AlarmManager. Each notification includes an action button. In the MainActivity, I have a TextView where I display a number counting how many times the action button was clicked.

There is a use case where displayed count number is not correct. To reproduce the issue I perform following steps:

  1. The app is minimized and I wait for notification.
  2. Click the action button
  3. go to the app tapping on the notification

The app opens itself however, the TextView displays still "0" (like in the initial state). However when I click BACK button on the device, the count number turns into a proper one.

When I click the notification, I get a warning which may be a reason of the problem: W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@c8e60ce

I was trying to fix that with this line in onCreate() method in MainActivity: mCount.setText(String.valueOf(mCorrectPostureReceiver.getCount())); But it doesn't work.

I should also mention that when I click the action button and then go to the app using launcher (not by tapping on the notification), everything works fine.

MainActivity.java

//
//imports
//

public class MainActivity extends AppCompatActivity {
    private static final String LOG_TAG = MainActivity.class.getSimpleName();
    private static String CORRECT_POSTURE_COUNT;
    private NotificationManager mNotifyManager;
    private static final int NOTIFICATION_ID = 0;
    private static final String PRIMARY_CHANNEL_ID = "primary_notification_channel";
    private CorrectPostureReceiver mCorrectPostureReceiver = new CorrectPostureReceiver(this);
    static final String POSTURE_YES_ACTION = BuildConfig.APPLICATION_ID + ".POSTURE_YES_ACTION";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        createNotificationChannel();

        ToggleButton mNotifToggle = findViewById(R.id.notify_toggle);
        TextView mCount = findViewById(R.id.txt_count);
        mCount.setText(String.valueOf(mCorrectPostureReceiver.getCount())); //doesn't work

        //set alarmPendingIntent to deliver repeating notifications
        final AlarmManager mAlarmManager = (AlarmManager) this.getSystemService(ALARM_SERVICE);
        Intent alarmIntent = new Intent(this, AlarmReceiver.class);
        mNotifToggle.setChecked(PendingIntent.getBroadcast(this, NOTIFICATION_ID, alarmIntent, PendingIntent.FLAG_NO_CREATE) != null); // check if notifications were turned on before the new MainActivity was stopped
        final PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(this, NOTIFICATION_ID, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        registerReceiver(mCorrectPostureReceiver, new IntentFilter(POSTURE_YES_ACTION));

        mNotifToggle.setOnCheckedChangeListener(
                new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        String toastMessage = "error: AlarmManager is null";
                        if(isChecked) {
                            long repeatInterval;

                            //is emulator or device
                            if(Build.FINGERPRINT.startsWith("google/sdk_gphone_x86/generic")) {
                                repeatInterval = 30000; //short interval only for debug }                  
                            else { repeatInterval = AlarmManager.INTERVAL_HALF_HOUR; }

                            long triggerTime = SystemClock.elapsedRealtime() + repeatInterval;
                            if(mAlarmManager!=null) {
                                mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, repeatInterval, alarmPendingIntent);
                                toastMessage = "Upright notifications on.";
                            }
                        }
                        else {
                            mNotifyManager.cancelAll();
                            if (mAlarmManager!=null) {
                                mAlarmManager.cancel(alarmPendingIntent);
                            }
                            toastMessage = "Upright notifications off.";
                        }
                        Toast.makeText(MainActivity.this, toastMessage, Toast.LENGTH_SHORT).show();
                    }
                }
        );
        Log.d(LOG_TAG, "A: created");
    }


    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
//        super.onRestoreInstanceState(savedInstanceState);  //should be commmented?
        mCorrectPostureReceiver.getTxtCount().setText(savedInstanceState.getString(CORRECT_POSTURE_COUNT));
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        outState.putString(CORRECT_POSTURE_COUNT, String.valueOf(mCorrectPostureReceiver.getCount()));
        super.onSaveInstanceState(outState);
    }

    private void createNotificationChannel() {
        mNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        long[] vibPattern = {0, 200, 200, 200, 200, 200}; //{delay1, vibDuration1, delay2, vibDuration2...}
        if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannel notificationChannel = new NotificationChannel(
                    PRIMARY_CHANNEL_ID,
                    "Check posture notification",
                    NotificationManager.IMPORTANCE_HIGH);
            notificationChannel.setDescription("notification check posture");
            notificationChannel.enableVibration(true);
            notificationChannel.setVibrationPattern(vibPattern);
            mNotifyManager.createNotificationChannel(notificationChannel);
        }
    }


}

AlarmReceiver.java - receives repeating intent messages from AlarmManager and delivers notifications

//
//imports
//

public class AlarmReceiver extends BroadcastReceiver {

    private NotificationManager mNotificationManager;
    private static final int NOTIFICATION_ID = 0;
    private static final String PRIMARY_CHANNEL_ID = "primary_notification_channel";
    private static final String LOG_TAG = AlarmReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        deliverNotification(context);
        Log.d(LOG_TAG, "notification fired!");
    }

    private void deliverNotification(Context context) {

        //this intent causes that when the notification is clicked, the MainActivity is launched
        Intent notifClickIntent = new Intent(context, MainActivity.class); //when the notification is clicked, the MainActivity is launched
        PendingIntent notifClickPendingIntent = PendingIntent.getActivity(
                context,
                NOTIFICATION_ID,
                notifClickIntent,
                PendingIntent.FLAG_UPDATE_CURRENT
        );
     
        // after you click the action button on notification, this intent will be sent
        Intent postureYesIntent = new Intent(MainActivity.POSTURE_YES_ACTION);
        PendingIntent yesPendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID, postureYesIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, PRIMARY_CHANNEL_ID)
                .setContentTitle("Are you straighten up?")
                .setSmallIcon(R.drawable.ic_notify)
                .setAutoCancel(true)
                .setContentIntent(notifClickPendingIntent)
                .addAction(R.drawable.ic_posture_yes, "yes", yesPendingIntent)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setDefaults(NotificationCompat.DEFAULT_ALL);
        mNotificationManager.notify(NOTIFICATION_ID, builder.build());
    }
}

CorrectPostureReceiver.java - gets an intent message when the action button on notification was clicked.

//
//imports
//

class CorrectPostureReceiver extends BroadcastReceiver {

    private int mCount = 0;
    private TextView mTxtCount;
    private final String LOG_TAG = CorrectPostureReceiver.class.getSimpleName();
    private MainActivity mainActivity;
    public CorrectPostureReceiver(MainActivity mainActivity) {
        this.mainActivity = mainActivity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(LOG_TAG, "yes option clicked");
        mTxtCount = mainActivity.findViewById(R.id.txt_count);
        if (mTxtCount != null) {
            mCount++;
            mTxtCount.setText(Integer.toString(mCount));
        }
    }

    public int getCount() {
        return mCount;
    }
    public TextView getTxtCount() {
        return mTxtCount;
    }
}

AndroidManifest.xml fragment

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>

How to fix that? If you have any other suggestions to my code, please write me about it.

2 Answers

When you click on the Notification, Android is launching a new instance of MainActivity. This is a completely different object than the original instance of MainActivity, which still exists and is being completely covered by the new one. That's the reason that the count shows zero, and also the reason why, when you press BACK, the count appears correct (after pressing BACK, the new instance of MainActivity is destroyed, revealing the previous original instance of MainActivity underneath it).

You should add Intent.FLAG_ACTIVITY_SINGLE_TOP to the Intent when you create the Notification:

    //this intent causes that when the notification is clicked, the MainActivity is launched
    Intent notifClickIntent = new Intent(context, MainActivity.class); //when the notification is clicked, the MainActivity is launched
    notifClickIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent notifClickPendingIntent = PendingIntent.getActivity(
            context,
            NOTIFICATION_ID,
            notifClickIntent,
            PendingIntent.FLAG_UPDATE_CURRENT
    );

Another solution is setting android:launchMode="singleInstance" in AndroidManifest.xml (in this case for MainActivity).

This is better than setting a flag FLAG_ACTIVITY_SINGLE_TOP (as in David Wasser's answer) because when you have more than one Activity and the other Activity is on the top of Activity stack and then you click on the notification, the issue from question will still occur.

Related