blob: f63b59008d0f98a6c424278851bf663061c10ec5 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.send_tab_to_self;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.provider.Browser;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.ShortcutHelper;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.chrome.browser.notifications.ChromeNotification;
import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
import org.chromium.chrome.browser.notifications.NotificationConstants;
import org.chromium.chrome.browser.notifications.NotificationManagerProxy;
import org.chromium.chrome.browser.notifications.NotificationManagerProxyImpl;
import org.chromium.chrome.browser.notifications.NotificationMetadata;
import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
import org.chromium.chrome.browser.notifications.PendingIntentProvider;
import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
import org.chromium.chrome.browser.profiles.Profile;
/**
* Manages all SendTabToSelf related notifications for Android. This includes displaying, handling
* taps, and timeouts.
*/
public class NotificationManager {
private static final String NOTIFICATION_GUID_EXTRA = "send_tab_to_self.notification.guid";
/** Records dismissal when notification is swiped away. */
public static final class DeleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String guid = intent.getStringExtra(NOTIFICATION_GUID_EXTRA);
hideNotification(guid);
SendTabToSelfAndroidBridge.dismissEntry(Profile.getLastUsedProfile(), guid);
}
}
/** Handles the tapping of a notification by opening the URL and hiding the notification. */
public static final class TapReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
openUrl(intent.getData());
String guid = intent.getStringExtra(NOTIFICATION_GUID_EXTRA);
hideNotification(guid);
SendTabToSelfAndroidBridge.deleteEntry(Profile.getLastUsedProfile(), guid);
}
}
/** Removes the notification after a timeout period. */
public static final class TimeoutReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String guid = intent.getStringExtra(NOTIFICATION_GUID_EXTRA);
hideNotification(guid);
SendTabToSelfAndroidBridge.dismissEntry(Profile.getLastUsedProfile(), guid);
}
}
/**
* Open the URL specified within Chrome.
*
* @param uri The URI to open.
*/
private static void openUrl(Uri uri) {
Context context = ContextUtils.getApplicationContext();
Intent intent = new Intent()
.setAction(Intent.ACTION_VIEW)
.setData(uri)
.setClass(context, ChromeLauncherActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())
.putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true);
IntentHandler.addTrustedIntentExtras(intent);
context.startActivity(intent);
}
/**
* Hides a notification and records an action to the Actions histogram.
* <p>
* If the notification is not actually visible, then no action will be taken, and the action
* will not be recorded.
*
* @param guid The GUID of the notification to hide.
*/
@CalledByNative
private static void hideNotification(@Nullable String guid) {
NotificationSharedPrefManager.ActiveNotification activeNotification =
NotificationSharedPrefManager.findActiveNotification(guid);
if (!NotificationSharedPrefManager.removeActiveNotification(guid)) {
return;
}
Context context = ContextUtils.getApplicationContext();
NotificationManagerProxy manager = new NotificationManagerProxyImpl(context);
manager.cancel(
NotificationConstants.GROUP_SEND_TAB_TO_SELF, activeNotification.notificationId);
}
/**
* Displays a notification.
*
* @param url URL to open when the user taps on the notification.
* @param title Title to display within the notification.
* @param timeoutAtMillis Specifies how long until the notification should be automatically
* hidden.
* @return whether the notification was successfully displayed
*/
@CalledByNative
private static boolean showNotification(String guid, @NonNull String url, String title,
String deviceName, long timeoutAtMillis) {
// A notification associated with this Share entry already exists. Don't display a new one.
if (NotificationSharedPrefManager.findActiveNotification(guid) != null) {
return false;
}
// Post notification.
Context context = ContextUtils.getApplicationContext();
NotificationManagerProxy manager = new NotificationManagerProxyImpl(context);
int nextId = NotificationSharedPrefManager.getNextNotificationId();
Uri uri = Uri.parse(url);
PendingIntentProvider contentIntent = PendingIntentProvider.getBroadcast(context, nextId,
new Intent(context, TapReceiver.class)
.setData(uri)
.putExtra(NOTIFICATION_GUID_EXTRA, guid),
0);
PendingIntentProvider deleteIntent = PendingIntentProvider.getBroadcast(context, nextId,
new Intent(context, DeleteReceiver.class)
.setData(uri)
.putExtra(NOTIFICATION_GUID_EXTRA, guid),
0);
// IDS_SEND_TAB_TO_SELF_NOTIFICATION_CONTEXT_TEXT
Resources res = context.getResources();
String contextText = res.getString(
R.string.send_tab_to_self_notification_context_text, uri.getHost(), deviceName);
// Build the notification itself.
ChromeNotificationBuilder builder =
NotificationBuilderFactory
.createChromeNotificationBuilder(true /* preferCompat */,
ChannelDefinitions.ChannelId.SHARING,
null /* remoteAppPackageName */,
new NotificationMetadata(
NotificationUmaTracker.SystemNotificationType
.SEND_TAB_TO_SELF,
NotificationConstants.GROUP_SEND_TAB_TO_SELF, nextId))
.setContentIntent(contentIntent)
.setDeleteIntent(deleteIntent)
.setContentTitle(title)
.setContentText(contextText)
.setGroup(NotificationConstants.GROUP_SEND_TAB_TO_SELF)
.setPriorityBeforeO(NotificationCompat.PRIORITY_HIGH)
.setVibrate(new long[0])
.setSmallIcon(R.drawable.ic_chrome)
.setDefaults(Notification.DEFAULT_ALL);
ChromeNotification notification = builder.buildChromeNotification();
manager.notify(notification);
NotificationUmaTracker.getInstance().onNotificationShown(
NotificationUmaTracker.SystemNotificationType.SEND_TAB_TO_SELF,
notification.getNotification());
NotificationSharedPrefManager.addActiveNotification(
new NotificationSharedPrefManager.ActiveNotification(nextId, guid));
// Set timeout.
if (timeoutAtMillis != Long.MAX_VALUE) {
AlarmManager alarmManager =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent timeoutIntent = new Intent(context, TimeoutReceiver.class)
.setData(Uri.parse(url))
.putExtra(NOTIFICATION_GUID_EXTRA, guid);
alarmManager.set(AlarmManager.RTC, timeoutAtMillis,
PendingIntent.getBroadcast(
context, nextId, timeoutIntent, PendingIntent.FLAG_UPDATE_CURRENT));
}
return true;
}
}