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:
- The app is minimized and I wait for notification.
- Click the action button
- 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.