/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.fenix.components import android.annotation.TargetApi import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build import android.os.Build.VERSION.SDK_INT import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService import mozilla.components.concept.sync.Device import mozilla.components.concept.sync.TabData import mozilla.components.support.base.log.logger.Logger import org.mozilla.fenix.R /** * Manages notification channels and allows displaying different types of notifications. */ class NotificationManager(private val context: Context) { companion object { const val RECEIVE_TABS_TAG = "ReceivedTabs" const val RECEIVE_TABS_CHANNEL_ID = "ReceivedTabsChannel" } init { // Create the notification channels we are going to use, but only on API 26+ because the NotificationChannel // class is new and not in the support library. if (SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel( RECEIVE_TABS_CHANNEL_ID, // Pick 'high' because this is a user-triggered action that is expected to be part of a continuity flow. // That is, user is expected to be waiting for this notification on their device; make it obvious. NotificationManager.IMPORTANCE_HIGH, // Name and description are shown in the 'app notifications' settings for the app. context.getString(R.string.fxa_received_tab_channel_name), context.getString(R.string.fxa_received_tab_channel_description) ) } } private val logger = Logger("NotificationManager") fun showReceivedTabs(context: Context, device: Device?, tabs: List) { // In the future, experiment with displaying multiple tabs from the same device as as Notification Groups. // For now, a single notification per tab received will suffice. logger.debug("Showing ${tabs.size} tab(s) received from deviceID=${device?.id}") tabs.forEach { tab -> val intent = Intent(Intent.ACTION_VIEW, Uri.parse(tab.url)) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.putExtra(RECEIVE_TABS_TAG, true) val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT) val builder = NotificationCompat.Builder(context, RECEIVE_TABS_CHANNEL_ID) .setSmallIcon(R.drawable.ic_status_logo) .setSendTabTitle(context, device, tab) .setWhen(System.currentTimeMillis()) .setContentText(tab.url) .setContentIntent(pendingIntent) .setAutoCancel(true) // Explicitly set a priority for = Build.VERSION_CODES.M) { builder.setCategory(Notification.CATEGORY_REMINDER) } val notification = builder.build() // Pick a random ID for this notification so that different tabs do not clash. @SuppressWarnings("MagicNumber") val notificationId = (Math.random() * 100).toInt() with(NotificationManagerCompat.from(context)) { notify(RECEIVE_TABS_TAG, notificationId, notification) } } } @TargetApi(Build.VERSION_CODES.O) private fun createNotificationChannel( channelId: String, importance: Int, channelName: String, channelDescription: String ) { val channel = NotificationChannel(channelId, channelName, importance).apply { description = channelDescription } // Register the channel with the system. Once this is done, we can't change importance or other notification // channel behaviour. We will be able to change 'name' and 'description' if we so choose. val notificationManager: NotificationManager = context.getSystemService()!! notificationManager.createNotificationChannel(channel) } private fun NotificationCompat.Builder.setSendTabTitle( context: Context, device: Device?, tab: TabData ): NotificationCompat.Builder { device?.let { setContentTitle( context.getString( R.string.fxa_tab_received_from_notification_name, it.displayName ) ) return this } if (tab.title.isEmpty()) { setContentTitle(context.getString(R.string.fxa_tab_received_notification_name)) } else { setContentTitle(tab.title) } return this } }