I am developing an android app and I want to send "We Miss You" notification to user if he/she is inactive for more than 3 days. This means if the user does not open our app for 3 days I want to send them a notification of "We Miss you" Like Deer Hunter and Temple Run sends Thank you :)
3 Answers
You want to set an alarm that starts a short-running service once per day. All that service does is to check something like a shared preference for the last-used-time. If that time is more than 3 days old, then the service sends a notification. Either way, the service then exits.
Alternatively, your app could submit an alarm, each time it runs, that is defined to fire in 3 days and defined to replace any pre-existing alarm with the same ID. That alarm would be defined to open a short-running service that sends a notification.
Here's some sample code that demonstrates the second approach. As written, it only updates the run time upon start-up. If your app is long-running, you'll want to call recordRunTime()
periodically. The deprecated method getNotification()
could be replaced with build()
if the Notification.Builder object was replaced with a NotificationCompat.Builder. You can use the commented out line for delay
to test with a shorter delay time.
MainActivity.java:
package com.example.comeback;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
private final static String TAG = "MainActivity";
public final static String PREFS = "PrefsFile";
private SharedPreferences settings = null;
private SharedPreferences.Editor editor = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
// Save time of run:
settings = getSharedPreferences(PREFS, MODE_PRIVATE);
editor = settings.edit();
// First time running app?
if (!settings.contains("lastRun"))
enableNotification(null);
else
recordRunTime();
Log.v(TAG, "Starting CheckRecentRun service...");
startService(new Intent(this, CheckRecentRun.class));
}
public void recordRunTime() {
editor.putLong("lastRun", System.currentTimeMillis());
editor.commit();
}
public void enableNotification(View v) {
editor.putLong("lastRun", System.currentTimeMillis());
editor.putBoolean("enabled", true);
editor.commit();
Log.v(TAG, "Notifications enabled");
}
public void disableNotification(View v) {
editor.putBoolean("enabled", false);
editor.commit();
Log.v(TAG, "Notifications disabled");
}
}
CheckRecentRun.java:
package com.example.comeback;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.util.Log;
public class CheckRecentRun extends Service {
private final static String TAG = "CheckRecentPlay";
private static Long MILLISECS_PER_DAY = 86400000L;
private static Long MILLISECS_PER_MIN = 60000L;
// private static long delay = MILLISECS_PER_MIN * 3; // 3 minutes (for testing)
private static long delay = MILLISECS_PER_DAY * 3; // 3 days
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "Service started");
SharedPreferences settings = getSharedPreferences(MainActivity.PREFS, MODE_PRIVATE);
// Are notifications enabled?
if (settings.getBoolean("enabled", true)) {
// Is it time for a notification?
if (settings.getLong("lastRun", Long.MAX_VALUE) < System.currentTimeMillis() - delay)
sendNotification();
} else {
Log.i(TAG, "Notifications are disabled");
}
// Set an alarm for the next time this service should run:
setAlarm();
Log.v(TAG, "Service stopped");
stopSelf();
}
public void setAlarm() {
Intent serviceIntent = new Intent(this, CheckRecentRun.class);
PendingIntent pi = PendingIntent.getService(this, 131313, serviceIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delay, pi);
Log.v(TAG, "Alarm set");
}
public void sendNotification() {
Intent mainIntent = new Intent(this, MainActivity.class);
@SuppressWarnings("deprecation")
Notification noti = new Notification.Builder(this)
.setAutoCancel(true)
.setContentIntent(PendingIntent.getActivity(this, 131314, mainIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
.setContentTitle("We Miss You!")
.setContentText("Please play our game again soon.")
.setDefaults(Notification.DEFAULT_ALL)
.setSmallIcon(R.drawable.ic_launcher)
.setTicker("We Miss You! Please come back and play our game again soon.")
.setWhen(System.currentTimeMillis())
.getNotification();
NotificationManager notificationManager
= (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(131315, noti);
Log.v(TAG, "Notification sent");
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
main_activity_layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="enableNotification"
android:text="@string/enable" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="disableNotification"
android:text="@string/disable" />
</LinearLayout>
strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Come Back</string>
<string name="enable">Enable Notifications</string>
<string name="disable">Disable Notifications</string>
</resources>
AndroidManifest.com:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.comeback"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.comeback.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.example.comeback.CheckRecentRun" >
</service>
</application>
</manifest>

- 8,301
- 1
- 31
- 41
-
Can you give a link or something? how to implement this? – Amin Motiwala Mar 28 '14 at 17:31
-
I added a demo app that demonstrates the second option in my answer. – scottt Mar 29 '14 at 06:42
-
1You are awesome :) – Alex Feb 05 '19 at 08:57
-
what about if app is closed ? – clauub Mar 21 '19 at 13:14
-
This answer was written with the idea that the application would be closed when the check occurred. While the latest versions of Android, with Doze mode and app standby limitations, curtail background services, this should still work since the service runs quickly. – scottt Mar 23 '19 at 04:55
You could have something like:
When a user opens the app, you record it on a database on your website and have a cron job (or some other equivalent) to run everyday at say 3pm time and if the users last open date is over 3 days ago you send the notification.
OR
When a user opens the app, you record it on a sqlite database on your app and set an alarm manager for 3 days time. Each time the app is open the alarm is reset. After the 3 days is up you create a notification.

- 12,291
- 19
- 47
- 72
I tried to make this feature with broadcast. Here is the implementation:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
MainActivity.kt
package com.example.myapplication
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private var sharedPreferences: SharedPreferences? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sharedPreferences = applicationContext.getSharedPreferences(MAIN_ACTIVITY_PREFS, Context.MODE_PRIVATE)
recordRunTime()
Log.i("MainActivity", "Starting com.example.myapplication.MyReceiver broadcast...")
sendBroadcast(Intent(this, MyReceiver::class.java))
}
fun recordRunTime() {
val editor = sharedPreferences?.edit()
editor?.putLong("lastRun", System.currentTimeMillis())
editor?.apply()
}
}
MyReceiver.kt
package com.example.myapplication
import android.app.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val settings = context.applicationContext.getSharedPreferences(MAIN_ACTIVITY_PREFS, Service.MODE_PRIVATE)
// Are notifications enabled?
if (settings.getBoolean(ENABLED, true)) {
// Is it time for a notification?
val channel = createChannel()
Log.i(TAG, "onReceive: ")
makeStatusNotification("Notification Message", context, channel)
Log.i(TAG, "onStartCommand: " + settings.getLong(LAST_RUN, -1))
setAlarm(context)
} else {
Log.i(TAG, "Notifications are disabled")
}
}
private fun createChannel(): NotificationChannel? {
var channel: NotificationChannel? = null
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
val name = VERBOSE_NOTIFICATION_CHANNEL_NAME
val description = VERBOSE_NOTIFICATION_CHANNEL_DESCRIPTION
val importance = NotificationManager.IMPORTANCE_HIGH
channel = NotificationChannel(CHANNEL_ID, name, importance)
channel.description = description
}
return channel
}
companion object {
fun setAlarm(context: Context) {
val broadcastIntent = Intent(context, MyReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(context.applicationContext, 121314, broadcastIntent,
PendingIntent.FLAG_CANCEL_CURRENT)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + delay, pendingIntent)
Log.i(TAG, "Alarm set")
}
const val TAG = "AlarmReceiver"
private const val MILLISECS_PER_MIN = 10000L
const val delay = MILLISECS_PER_MIN * 1// 0.5 minutes (for testing)
//private const val MILLISECS_PER_DAY = 86400000L
//private const val delay = MILLISECS_PER_DAY * 3 // 3 days
const val VERBOSE_NOTIFICATION_CHANNEL_NAME =
"Check Last Run"
const val VERBOSE_NOTIFICATION_CHANNEL_DESCRIPTION =
"Shows notifications if the user didn't open the app for 3 day"
const val NOTIFICATION_TITLE = "Notification Title"
const val CHANNEL_ID = "VERBOSE_NOTIFICATION"
const val NOTIFICATION_ID = 1021
const val ENABLED = "is_enabled"
}
}
Constants.kt
package com.example.myapplication
const val MAIN_ACTIVITY_PREFS = "mainPrefs"
const val LAST_RUN = "lastRun"
NotificationHelper.kt
package com.example.myapplication
import com.example.myapplication.MyReceiver.Companion.CHANNEL_ID
import com.example.myapplication.MyReceiver.Companion.NOTIFICATION_ID
import com.example.myapplication.MyReceiver.Companion.NOTIFICATION_TITLE
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import java.lang.NullPointerException
fun makeStatusNotification(message: String, context: Context, channel: NotificationChannel?) {
// Make a channel if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Add the channel
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (channel != null)
notificationManager.createNotificationChannel(channel)
else
throw NullPointerException("Notification Channel can't be NULL")
}
// Create the notification
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle(NOTIFICATION_TITLE)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVibrate(LongArray(1))
.setCategory(NotificationCompat.CATEGORY_ALARM)
// Show the notification
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, builder.build())
}
Hoping you find this helpful.

- 1
- 1
-
I tried this solution and it does not work for me. I am using 30 seconds for testing and every time I run the app, the notification pops up every 30 seconds even if I am actively using the app. It does not detect inactivity – A.S Jul 22 '23 at 02:59