/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.documentsui.services; import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ProgressBar; import android.widget.RemoteViews; /** * This class receives a callback when Notification is posted or removed * and monitors the Notification status. * And, this sends the operation's result by Broadcast. */ public class TestNotificationService extends NotificationListenerService { private static final String TAG = "TestNotificationService"; public static final String ACTION_CHANGE_CANCEL_MODE = "com.android.documentsui.services.TestNotificationService.ACTION_CHANGE_CANCEL_MODE"; public static final String ACTION_CHANGE_EXECUTION_MODE = "com.android.documentsui.services.TestNotificationService.ACTION_CHANGE_EXECUTION_MODE"; public static final String ACTION_OPERATION_RESULT = "com.android.documentsui.services.TestNotificationService.ACTION_OPERATION_RESULT"; public static final String ANDROID_PACKAGENAME = "android"; public static final String CANCEL_RES_NAME = "cancel"; public static final String EXTRA_RESULT = "com.android.documentsui.services.TestNotificationService.EXTRA_RESULT"; public static final String EXTRA_ERROR_REASON = "com.android.documentsui.services.TestNotificationService.EXTRA_ERROR_REASON"; public enum MODE { CANCEL_MODE, EXECUTION_MODE; } private static String mTargetPackageName; private MODE mCurrentMode = MODE.CANCEL_MODE; private boolean mCancelled = false; private FrameLayout mFrameLayout = null; private ProgressBar mProgressBar = null; private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_CHANGE_CANCEL_MODE.equals(action)) { Log.i(TAG, "Received cancel mode"); mCurrentMode = MODE.CANCEL_MODE; } else if (ACTION_CHANGE_EXECUTION_MODE.equals(action)) { Log.i(TAG, "Received execution mode"); mCurrentMode = MODE.EXECUTION_MODE; } } }; @Override public void onCreate() { mTargetPackageName = getTargetPackageName(); mFrameLayout = new FrameLayout(getBaseContext()); IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_CHANGE_CANCEL_MODE); filter.addAction(ACTION_CHANGE_EXECUTION_MODE); registerReceiver(mReceiver, filter); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } @Override public void onDestroy() { unregisterReceiver(mReceiver); mProgressBar = null; mFrameLayout.removeAllViews(); mFrameLayout = null; } @Override public void onNotificationPosted(StatusBarNotification sbn) { String pkgName = sbn.getPackageName(); Log.i(TAG, "Entering notification posted cancelMode = " + MODE.CANCEL_MODE .equals(mCurrentMode) + " targetPackageName " + mTargetPackageName + " packageName " + pkgName); if (mTargetPackageName.equals(pkgName)) { if (MODE.CANCEL_MODE.equals(mCurrentMode)) { try { mCancelled = doCancel(sbn.getNotification()); } catch (Exception e) { Log.d(TAG, "Error occurs when cancel notification.", e); } } } } @Override public void onNotificationRemoved(StatusBarNotification sbn) { Log.i(TAG, "Entering notification removed cancelMode = " + MODE.CANCEL_MODE .equals(mCurrentMode)); String pkgName = sbn.getPackageName(); if (!mTargetPackageName.equals(pkgName)) { return; } Intent intent = new Intent(ACTION_OPERATION_RESULT); if (MODE.CANCEL_MODE.equals(mCurrentMode)) { intent.putExtra(EXTRA_RESULT, mCancelled); if (!mCancelled) { intent.putExtra(EXTRA_ERROR_REASON, "Cannot executed cancel"); } } else if (MODE.EXECUTION_MODE.equals(mCurrentMode)) { boolean isStartProgress = isStartProgress(sbn.getNotification()); intent.putExtra(EXTRA_RESULT, isStartProgress); if (!isStartProgress) { intent.putExtra(EXTRA_ERROR_REASON, "Progress does not displayed correctly."); } } sendBroadcast(intent); } private boolean doCancel(Notification noti) throws NameNotFoundException, PendingIntent.CanceledException { if (!isStartProgress(noti)) { return false; } Notification.Action aList [] = noti.actions; if (aList == null) { return false; } boolean result = false; for (Notification.Action item : aList) { Context android_context = getBaseContext().createPackageContext(ANDROID_PACKAGENAME, Context.CONTEXT_RESTRICTED); int res_id = android_context.getResources().getIdentifier(CANCEL_RES_NAME, "string", ANDROID_PACKAGENAME); final String cancel_label = android_context.getResources().getString(res_id); if (cancel_label.equals(item.title)) { item.actionIntent.send(); result = true; } } return result; } private boolean isStartProgress(Notification notifiction) { ProgressBar progressBar = getProgresssBar(getRemoteViews(notifiction)); return (progressBar != null) ? progressBar.getProgress() > 0 : false; } private RemoteViews getRemoteViews(Notification notifiction) { Notification.Builder builder = Notification.Builder.recoverBuilder( getBaseContext(), notifiction); if (builder == null) { return null; } return builder.createContentView(); } private ProgressBar getProgresssBar(RemoteViews remoteViews) { if (remoteViews == null) { return null; } View view = remoteViews.apply(getBaseContext(), mFrameLayout); return getProgressBarImpl(view); } private ProgressBar getProgressBarImpl(View view) { if (view == null || !(view instanceof ViewGroup)) { return null; } ViewGroup viewGroup = (ViewGroup)view; if (viewGroup.getChildCount() <= 0) { return null; } ProgressBar result = null; for (int i = 0; i < viewGroup.getChildCount(); i++) { View v = viewGroup.getChildAt(i); if (v instanceof ProgressBar) { result = ((ProgressBar)v); break; } else if (v instanceof ViewGroup) { result = getProgressBarImpl(v); if (result != null) { break; } } } return result; } private String getTargetPackageName() { final PackageManager pm = getPackageManager(); final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); final ResolveInfo ri = pm.resolveActivity(intent, 0); return ri.activityInfo.packageName; } }