/* 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.gecko.tabqueue; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.BrowserLocaleManager; import org.mozilla.gecko.GeckoSharedPrefs; import org.mozilla.gecko.R; import org.mozilla.gecko.db.BrowserContract; import android.app.IntentService; import android.app.PendingIntent; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.database.Cursor; import android.media.RingtoneManager; import android.net.Uri; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; import android.util.Log; /** * An IntentService that displays a notification for a tab sent to this device. * * The expected Intent should contain: * * Data: URI to open in the notification * * EXTRA_TITLE: Page title of the URI to open */ public class TabReceivedService extends IntentService { private static final String LOGTAG = "Gecko" + TabReceivedService.class.getSimpleName(); private static final String PREF_NOTIFICATION_ID = "tab_received_notification_id"; private static final int MAX_NOTIFICATION_COUNT = 1000; public TabReceivedService() { super(LOGTAG); setIntentRedelivery(true); } @Override protected void onHandleIntent(final Intent intent) { // IntentServices don't keep the process alive so // we need to do this every time. Ideally, we wouldn't. final Resources res = getResources(); BrowserLocaleManager.getInstance().correctLocale(this, res, res.getConfiguration()); final String uri = intent.getDataString(); if (uri == null) { Log.d(LOGTAG, "Received null uri – ignoring"); return; } final Intent notificationIntent = new Intent(Intent.ACTION_VIEW, intent.getData()); notificationIntent.putExtra(BrowserContract.SKIP_TAB_QUEUE_FLAG, true); final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); final String notificationTitle = getNotificationTitle(intent.getStringExtra(BrowserContract.EXTRA_CLIENT_GUID)); final NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setSmallIcon(R.drawable.flat_icon); builder.setContentTitle(notificationTitle); builder.setWhen(System.currentTimeMillis()); builder.setAutoCancel(true); builder.setContentText(uri); builder.setContentIntent(contentIntent); // Trigger "heads-up" notification mode on supported Android versions. builder.setPriority(NotificationCompat.PRIORITY_HIGH); final Uri notificationSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); if (notificationSoundUri != null) { builder.setSound(notificationSoundUri); } final SharedPreferences prefs = GeckoSharedPrefs.forApp(this); final int notificationId = getNextNotificationId(prefs.getInt(PREF_NOTIFICATION_ID, 0)); final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); notificationManager.notify(notificationId, builder.build()); // Save the ID last so if the Service is killed and the Intent is redelivered, // the ID is unlikely to have been updated and we would re-use the the old one. // This would prevent two identical notifications from appearing if the // notification was shown during the previous Intent processing attempt. prefs.edit().putInt(PREF_NOTIFICATION_ID, notificationId).apply(); } /** * @param clientGUID the guid of the client in the clients table * @return the client's name from the clients table, if possible, else the brand name. */ @WorkerThread private String getNotificationTitle(@Nullable final String clientGUID) { if (clientGUID == null) { Log.w(LOGTAG, "Received null guid, using brand name."); return AppConstants.MOZ_APP_DISPLAYNAME; } final Cursor c = getContentResolver().query(BrowserContract.Clients.CONTENT_URI, new String[] { BrowserContract.Clients.NAME }, BrowserContract.Clients.GUID + "=?", new String[] { clientGUID }, null); try { if (c != null && c.moveToFirst()) { return c.getString(c.getColumnIndex(BrowserContract.Clients.NAME)); } else { Log.w(LOGTAG, "Device not found, using brand name."); return AppConstants.MOZ_APP_DISPLAYNAME; } } finally { if (c != null) { c.close(); } } } /** * Notification IDs must be unique else a notification * will be overwritten so we cycle them. */ private int getNextNotificationId(final int currentId) { if (currentId > MAX_NOTIFICATION_COUNT) { return 0; } else { return currentId + 1; } } }