Dragos Toader
Dragos Toader

Reputation: 124

How to make a notification at an exact date and time in Kotlin, Android

After reading about 20 articles on this site, the official Android Developers site and elsewhere, it seems that I cannot find how to pass the exact date and time to fire a notification in Android and Kotlin

Does the notification builder has a method in which I can pass this time or should I keep trying with Alarm Receiver? I am not sure how to instantiate Alarm receiver in the ViewModel

AlarmReceiver


import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Context.ALARM_SERVICE
import android.content.Intent
import com.example.spiritualvietnam.model.SpiritViewModel
import java.util.*

abstract class AlarmReceiver : BroadcastReceiver() {

    abstract val viewModel: SpiritViewModel
    override fun onReceive(context: Context, intent: Intent) {
        println("dragos am ajuns la broadcast receiver si am transmis notificarea")
        viewModel.showNotification(context)
        // implement showing notification in this function
        val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager
        val alarmPendingIntent by lazy {
            val intent = Intent(context, AlarmReceiver::class.java)
            PendingIntent.getBroadcast(context, 0, intent, 0)
        }
        val HOUR_TO_SHOW_PUSH = viewModel.hourOfNotification.value
        val MINUTE_TO_SHOW_PUSH = viewModel.minuteOfNotification.value

        fun schedulePushNotifications() {
            val calendar = GregorianCalendar.getInstance().apply {
                if (get(Calendar.HOUR_OF_DAY) >= HOUR_TO_SHOW_PUSH!!) {
                    add(Calendar.DAY_OF_MONTH, 1)
                }

                set(Calendar.HOUR_OF_DAY, HOUR_TO_SHOW_PUSH)
                if (MINUTE_TO_SHOW_PUSH != null) {
                    set(Calendar.MINUTE, MINUTE_TO_SHOW_PUSH)
                }
                set(Calendar.SECOND, 0)
                set(Calendar.MILLISECOND, 0)
            }

            alarmManager.setRepeating(
                AlarmManager.RTC_WAKEUP,
                calendar.timeInMillis,
                AlarmManager.INTERVAL_DAY,
                alarmPendingIntent
            )
        }

        if (intent.action == "android.intent.action.BOOT_COMPLETED") {
            schedulePushNotifications()
        }
    }
}

ViewModel

class SpiritViewModel: ViewModel() {

    // at what hour should the notification appear
    private var _hourOfNotification = MutableLiveData(18)
    var hourOfNotification: LiveData<Int> = _hourOfNotification

    // at what minute should the notification appear
    private val _minuteOfNotification = MutableLiveData(30)
    val minuteOfNotification: LiveData<Int> = _minuteOfNotification

   // used for the notification
    private companion object {
        private const val CHANNEL_ID = "channel01"
    }

    // Push the notification here - mainly boilerplate copy-pastable code
    fun showNotification(context: Context) {
        createNotificationChannel(context)

        val pattern = _hourOfNotification.value.toString() + _minuteOfNotification.value.toString() + "00"
       
        val date = Date()

        val notificationId = SimpleDateFormat(pattern).format(date).toInt()
        println(" dragos notification id is "+ notificationId)

        //handle notification click- should start SliderFragment Directly
        val mainIntent = Intent(context, SlidingPhotosFragment::class.java)
        
        mainIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

        //  here we use a NavDeepLinkBuilder from the Navigation subsection
        val pendingIntent = NavDeepLinkBuilder(context)
            .setComponentName(MainActivity::class.java)
            .setGraph(R.navigation.mobile_navigation)
            .setDestination(R.id.navigation_sliders)
            .createPendingIntent()

        // creating the notification builder
        val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ID)
        notificationBuilder.setSmallIcon(R.drawable.ic_home_icon)
        notificationBuilder.setContentTitle("Wait...")
        notificationBuilder.setContentText("Give yourself a break by immersing in beautiful Vietnam")
        notificationBuilder.priority = NotificationCompat.PRIORITY_HIGH
        //cancel notification on click
        notificationBuilder.setAutoCancel(true)
        // add click intent
        notificationBuilder.setContentIntent(pendingIntent)

        //notification manager
        val notificationManagerCompat = NotificationManagerCompat.from(context)
        notificationManagerCompat.notify(notificationId, notificationBuilder.build())
    }

    //     for api > 26 we need a notification channel
    fun createNotificationChannel(context: Context){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name: CharSequence = "MyNotification"
            val description = "The notification channel description"
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val notificationChannel = NotificationChannel(CHANNEL_ID, name, importance)
            notificationChannel.description = description
            // NOTIFICATION_SERVICE
            val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }

}

The notification is started in a fragment when a button is clicked, but should be activated with the alarm receiver class

            context?.let { viewModel.showNotification(it) }

AndroidManifest

<receiver android:name="com.example.spiritualvietnam.broadcast.AlarmReceiver" android:enabled="true"/>
        <receiver android:name="com.example.spiritualvietnam.broadcast.BootReceiver" android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

The notification appears only at the present time. I want to change somehow this time with a different one, to appear daily, even if the application is off. If I have to learn more about a specific topic please let me know!

Upvotes: 1

Views: 1643

Answers (1)

Anandh Krishnan
Anandh Krishnan

Reputation: 6022

activity_main.xml

<? 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 :layout_width = "match_parent"
   android :layout_height = "match_parent"
   android :padding = "16dp"
   tools :context = ".MainActivity" >
   <TextView
      android :id = "@+id/tvDate"
      android :layout_width = "match_parent"
      android :layout_height = "wrap_content"
      android :hint = "Select Date"
      android :onClick = "setDate"
      android :padding = "16dp" />
</RelativeLayout>

MainActivity

import android.app.AlarmManager ;
import android.app.DatePickerDialog ;
import android.app.Notification ;
import android.app.PendingIntent ;
import android.content.Context ;
import android.content.Intent ;
import android.os.Bundle ;
import android.support.v4.app.NotificationCompat ;
import android.support.v7.app.AppCompatActivity ;
import android.view.View ;
import android.widget.Button ;
import android.widget.DatePicker ;
import java.text.SimpleDateFormat ;
import java.util.Calendar ;
import java.util.Date ;
import java.util.Locale ;
public class MainActivity extends AppCompatActivity {
   public static final String NOTIFICATION_CHANNEL_ID = "10001" ;
   private final static String default_notification_channel_id = "default" ;
   Button btnDate ;
   final Calendar myCalendar = Calendar. getInstance () ;
   @Override
   protected void onCreate (Bundle savedInstanceState) {
      super .onCreate(savedInstanceState) ;
      setContentView(R.layout. activity_main ) ;
      btnDate = findViewById(R.id. btnDate ) ;
   }
   private void scheduleNotification (Notification notification , long delay) {
      Intent notificationIntent = new Intent( this, MyNotificationPublisher. class ) ;
      notificationIntent.putExtra(MyNotificationPublisher. NOTIFICATION_ID , 1 ) ;
      notificationIntent.putExtra(MyNotificationPublisher. NOTIFICATION , notification) ;
      PendingIntent pendingIntent = PendingIntent. getBroadcast ( this, 0 , notificationIntent , PendingIntent. FLAG_UPDATE_CURRENT ) ;
      AlarmManager alarmManager = (AlarmManager) getSystemService(Context. ALARM_SERVICE ) ;
      assert alarmManager != null;
      alarmManager.set(AlarmManager. ELAPSED_REALTIME_WAKEUP , delay , pendingIntent) ;
   }
   private Notification getNotification (String content) {
      NotificationCompat.Builder builder = new NotificationCompat.Builder( this, default_notification_channel_id ) ;
      builder.setContentTitle( "Scheduled Notification" ) ;
      builder.setContentText(content) ;
      builder.setSmallIcon(R.drawable. ic_launcher_foreground ) ;
      builder.setAutoCancel( true ) ;
      builder.setChannelId( NOTIFICATION_CHANNEL_ID ) ;
      return builder.build() ;
   }
   DatePickerDialog.OnDateSetListener date = new DatePickerDialog.OnDateSetListener() {
      @Override
      public void onDateSet (DatePicker view , int year , int monthOfYear , int dayOfMonth) {
         myCalendar .set(Calendar. YEAR , year) ;
         myCalendar .set(Calendar. MONTH , monthOfYear) ;
         myCalendar .set(Calendar. DAY_OF_MONTH , dayOfMonth) ;
         updateLabel() ;
      }
   } ;
   public void setDate (View view) {
      new DatePickerDialog(
         MainActivity. this, date ,
         myCalendar .get(Calendar. YEAR ) ,
         myCalendar .get(Calendar. MONTH ) ,
         myCalendar .get(Calendar. DAY_OF_MONTH )
      ).show() ;
   }
   private void updateLabel () {
      String myFormat = "dd/MM/yy" ; //In which you need put here
      SimpleDateFormat sdf = new SimpleDateFormat(myFormat , Locale. getDefault ()) ;
      Date date = myCalendar .getTime() ;
      btnDate .setText(sdf.format(date)) ;
      scheduleNotification(getNotification( btnDate .getText().toString()) , date.getTime()) ;
   }
}

MyNotificationPublisher

import android.app.Notification ;
import android.app.NotificationChannel ;
import android.app.NotificationManager ;
import android.content.BroadcastReceiver ;
import android.content.Context ;
import android.content.Intent ;
import static app.tutorialspoint.com.notifyme.MainActivity. NOTIFICATION_CHANNEL_ID ;
public class MyNotificationPublisher extends BroadcastReceiver {
   public static String NOTIFICATION_ID = "notification-id" ;
   public static String NOTIFICATION = "notification" ;
   public void onReceive (Context context , Intent intent) {
      NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context. NOTIFICATION_SERVICE ) ;
      Notification notification = intent.getParcelableExtra( NOTIFICATION ) ;
      if (android.os.Build.VERSION. SDK_INT >= android.os.Build.VERSION_CODES. O ) {
         int importance = NotificationManager. IMPORTANCE_HIGH ;
         NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "NOTIFICATION_CHANNEL_NAME" , importance) ;
         assert notificationManager != null;
         notificationManager.createNotificationChannel(notificationChannel) ;
      }
      int id = intent.getIntExtra( NOTIFICATION_ID , 0 ) ;
      assert notificationManager != null;
      notificationManager.notify(id , notification) ;
   }
}

AndroidManifest.xml

<? xml version = "1.0" encoding = "utf-8" ?>
<manifest xmlns: android = "http://schemas.android.com/apk/res/android"
   package = "app.tutorialspoint.com.notifyme" >
   <uses-permission android :name = "android.permission.VIBRATE" />
   <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/AppTheme" >
      <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= ".MyNotificationPublisher" />
   </application>
</manifest>

Upvotes: 0

Related Questions