/****************************************************************************** * Copyright (C) 2012, 2013, 2014, 2015, 2016 * Younghyung Cho. <yhcting77@gmail.com> * All rights reserved. * * This file is part of NetMBuddy * * This program is licensed under the FreeBSD license * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation * are those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of the FreeBSD Project. *****************************************************************************/ package free.yhc.netmbuddy.core; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.support.v4.app.NotificationCompat; import android.widget.RemoteViews; import free.yhc.abaselib.AppEnv; import free.yhc.baselib.Logger; import free.yhc.abaselib.util.AUtil; import free.yhc.netmbuddy.R; import free.yhc.netmbuddy.YTMPActivity; import free.yhc.netmbuddy.utils.Util; public class NotiManager { private static final boolean DBG = Logger.DBG_DEFAULT; private static final Logger P = Logger.create(NotiManager.class, Logger.LOGLV_DEFAULT); private static final String NOTI_INTENT_DELETE = "ytmplayer.intent.action.NOTIFICATION_DELETE"; private static final String NOTI_INTENT_ACTION = "ytmplayer.intent.action.NOTIFICATION_ACTION"; private static final String NOTI_INTENT_STOP_PLAYER = "ytmplayer.intent.action.NOTIFICATION_STOP_PLAYER"; private static NotiManager sInstance = null; // active notification set. private final NotificationManager mNm = (NotificationManager)AppEnv.getAppContext().getSystemService(Context.NOTIFICATION_SERVICE); private Notification mLastPlayerNotification = null; // NOTE public enum NotiType { // All player type notification uses same notification id because // these notification SHOULD NOT be multiple-displayed. BASE (R.drawable.noti_base, getPlayerNotificationId()), START (R.drawable.noti_start, getPlayerNotificationId()), PAUSE (R.drawable.noti_pause, getPlayerNotificationId()), STOP (R.drawable.noti_stop, getPlayerNotificationId()), ALERT (R.drawable.noti_alert, getPlayerNotificationId()), // General notification - not used yet. // Why? // In case of GMail, ImportShareActivity seems to be started with NEW_TASK. // See below steps. // - 1. After starting importing, ImportShareActivity is running foreground. // - 2. Backing to launcher by touching 'Home' button. // - 3a. Touch GMail icon again to go to GMail. // => ImportShareActivity is not shown anymore! // - 3b. Touch NetMBuddy to see ImportShareActivity // => ImportShareActivity is not shown any more. // As described above, there is no way to back to ImportShareActivity // after backing to launcher by touching 'Home' key. // // To workaround this, notification may be considered. // But, it doesn't help too. // Resuming app. or starting ImportShareActivity with singleTop // always tries to start new Activity instance. // I didn't find any solution for this issue. // So, by any reasonable solution is found, this notification is left as 'UNUSED'. IMPORT (R.drawable.noti_import,R.drawable.noti_import); private final int _mIcon; private final int _mId; NotiType(int icon, int id) { _mIcon = icon; _mId = id; } int getId() { return _mId; } int getIcon() { return _mIcon; } } public static class NotiIntentReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, final Intent intent) { AppEnv.getUiHandler().post(new Runnable() { @Override public void run() { handleNotificationIntent(intent); } }); } } private NotiManager() { } private static void handleNotificationIntent(Intent intent) { String action = intent.getAction(); NotiManager nm = NotiManager.get(); YTPlayer ytp = YTPlayer.get(); if (NOTI_INTENT_DELETE.equals(action)) nm.removePlayerNotification(); else if (NOTI_INTENT_STOP_PLAYER.equals(action)) ytp.stopVideos(); else if (NOTI_INTENT_ACTION.equals(action)) { String typeName = intent.getStringExtra("type"); if (DBG) P.v("Intent action type : " + typeName); P.bug(null != typeName); NotiType nt = NotiType.valueOf(typeName); P.bug(null != nt); assert null != nt; switch (nt) { case BASE: // Ignore this action!! break; case START: ytp.playerStart(); break; case PAUSE: ytp.playerPause(); break; case STOP: ytp.stopVideos(); break; case ALERT: // Just remove notification nm.removePlayerNotification(); break; case IMPORT: Util.resumeApp(); break; default: P.bug(false); } } } Notification buildNotification(NotiType ntype, CharSequence videoTitle) { RemoteViews rv = new RemoteViews(AppEnv.getAppContext().getPackageName(), R.layout.player_notification); NotificationCompat.Builder nbldr = new NotificationCompat.Builder(AppEnv.getAppContext()); rv.setTextViewText(R.id.title, videoTitle); Intent intent = new Intent(AppEnv.getAppContext(), NotiManager.NotiIntentReceiver.class); intent.setAction(NOTI_INTENT_ACTION); intent.putExtra("type", ntype.name()); PendingIntent pi = PendingIntent.getBroadcast(AppEnv.getAppContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setImageViewResource(R.id.action, ntype.getIcon()); rv.setOnClickPendingIntent(R.id.action, pi); intent = new Intent(AppEnv.getAppContext(), NotiManager.NotiIntentReceiver.class); intent.setAction(NOTI_INTENT_STOP_PLAYER); pi = PendingIntent.getBroadcast(AppEnv.getAppContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setImageViewResource(R.id.stop, R.drawable.ic_media_stop); rv.setOnClickPendingIntent(R.id.stop, pi); intent = new Intent(AppEnv.getAppContext(), NotiManager.NotiIntentReceiver.class); intent.setAction(NOTI_INTENT_DELETE); PendingIntent piDelete = PendingIntent.getBroadcast(AppEnv.getAppContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); intent = new Intent(AppEnv.getAppContext(), YTMPActivity.class); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); PendingIntent piContent = PendingIntent.getActivity(AppEnv.getAppContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); nbldr.setContent(rv) .setContentIntent(piContent) .setDeleteIntent(piDelete) .setWhen(System.currentTimeMillis()) .setAutoCancel(true) .setSmallIcon(ntype.getIcon()) .setTicker(null); return nbldr.build(); } public static NotiManager get() { if (null == sInstance) sInstance = new NotiManager(); return sInstance; } public static int getPlayerNotificationId() { return R.drawable.noti_base; } public void removeNotification(NotiType type) { // To avoid unexpected race-condition. P.bug(AUtil.isUiThread()); // Player notification shares same notification id. mNm.cancel(type.getId()); } public void removePlayerNotification() { removeNotification(NotiType.BASE); mLastPlayerNotification = null; } public void putNotification(NotiType type, String videoTitle) { // To avoid unexpected race-condition. P.bug(AUtil.isUiThread()); // Set event time. Notification n = buildNotification(type, videoTitle); n.when = System.currentTimeMillis(); mNm.notify(type.getId(), n); switch (type) { case BASE: case START: case PAUSE: case STOP: case ALERT: // In case of player notification mLastPlayerNotification = n; default: // ignored for other cases. } } public void putPlayerNotification(NotiType type, String videoTitle) { putNotification(type, videoTitle); } public Notification getLastPlayerNotification() { return mLastPlayerNotification; } }