1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.documentsui.services; 17 18 import android.app.Notification; 19 import android.app.PendingIntent; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.content.BroadcastReceiver; 24 import android.service.notification.NotificationListenerService; 25 import android.service.notification.StatusBarNotification; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.widget.FrameLayout; 29 import android.widget.ProgressBar; 30 import android.widget.RemoteViews; 31 32 33 /** 34 * This class receives a callback when Notification is posted or removed 35 * and monitors the Notification status. 36 * And, this sends the operation's result by Broadcast. 37 */ 38 public class TestNotificationService extends NotificationListenerService { 39 public static final String ACTION_CHANGE_CANCEL_MODE = 40 "com.android.documentsui.services.TestNotificationService.ACTION_CHANGE_CANCEL_MODE"; 41 42 public static final String ACTION_CHANGE_EXECUTION_MODE = 43 "com.android.documentsui.services.TestNotificationService.ACTION_CHANGE_EXECUTION_MODE"; 44 45 public static final String ACTION_OPERATION_RESULT = 46 "com.android.documentsui.services.TestNotificationService.ACTION_OPERATION_RESULT"; 47 48 public static final String EXTRA_RESULT = 49 "com.android.documentsui.services.TestNotificationService.EXTRA_RESULT"; 50 51 public static final String EXTRA_ERROR_REASON = 52 "com.android.documentsui.services.TestNotificationService.EXTRA_ERROR_REASON"; 53 54 public enum MODE { 55 CANCEL_MODE, 56 EXECUTION_MODE; 57 } 58 59 private String DOCUMENTSUI= "com.android.documentsui"; 60 61 private FrameLayout mFrameLayout = null; 62 63 private ProgressBar mProgressBar = null; 64 65 private MODE mCurrentMode = MODE.CANCEL_MODE; 66 67 private boolean mCancelled = false; 68 69 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 70 @Override 71 public void onReceive(Context context, Intent intent) { 72 String action = intent.getAction(); 73 if (ACTION_CHANGE_CANCEL_MODE.equals(action)) { 74 mCurrentMode = MODE.CANCEL_MODE; 75 } else if (ACTION_CHANGE_EXECUTION_MODE.equals(action)) { 76 mCurrentMode = MODE.EXECUTION_MODE; 77 } 78 } 79 }; 80 81 @Override 82 public void onCreate() { 83 mFrameLayout = new FrameLayout(getBaseContext()); 84 IntentFilter filter = new IntentFilter(); 85 filter.addAction(ACTION_CHANGE_CANCEL_MODE); 86 filter.addAction(ACTION_CHANGE_EXECUTION_MODE); 87 registerReceiver(mReceiver, filter); 88 } 89 90 @Override 91 public int onStartCommand(Intent intent, int flags, int startId) { 92 return START_STICKY; 93 } 94 95 @Override 96 public void onDestroy() { 97 unregisterReceiver(mReceiver); 98 mProgressBar = null; 99 mFrameLayout.removeAllViews(); 100 mFrameLayout = null; 101 } 102 103 @Override 104 public void onNotificationPosted(StatusBarNotification sbn) { 105 String pkgName = sbn.getPackageName(); 106 if (!pkgName.equals(DOCUMENTSUI)) { 107 return; 108 } 109 110 if (MODE.CANCEL_MODE.equals(mCurrentMode)) { 111 mCancelled = doCancel(sbn.getNotification()); 112 } 113 } 114 115 @Override 116 public void onNotificationRemoved(StatusBarNotification sbn) { 117 String pkgName = sbn.getPackageName(); 118 if (!pkgName.equals(DOCUMENTSUI)) { 119 return; 120 } 121 122 Intent intent = new Intent(ACTION_OPERATION_RESULT); 123 if (MODE.CANCEL_MODE.equals(mCurrentMode)) { 124 intent.putExtra(EXTRA_RESULT, mCancelled); 125 if (!mCancelled) { 126 intent.putExtra(EXTRA_ERROR_REASON, "Cannot executed cancel"); 127 } 128 } else if (MODE.EXECUTION_MODE.equals(mCurrentMode)) { 129 boolean isStartProgress = isStartProgress(sbn.getNotification()); 130 intent.putExtra(EXTRA_RESULT, isStartProgress); 131 if (!isStartProgress) { 132 intent.putExtra(EXTRA_ERROR_REASON, "Progress does not displayed correctly."); 133 } 134 } 135 sendBroadcast(intent); 136 } 137 138 private boolean doCancel(Notification noti) { 139 if (!isStartProgress(noti)) { 140 return false; 141 } 142 143 Notification.Action aList [] = noti.actions; 144 if (aList == null) { 145 return false; 146 } 147 148 boolean result = false; 149 for (Notification.Action item : aList) { 150 if (item.title.equals("Cancel")) { 151 try { 152 item.actionIntent.send(); 153 result = true; 154 } catch (PendingIntent.CanceledException e) { 155 } 156 } 157 } 158 return result; 159 } 160 161 private boolean isStartProgress(Notification notifiction) { 162 ProgressBar progressBar = getProgresssBar(getRemoteViews(notifiction)); 163 return (progressBar != null) ? progressBar.getProgress() > 0 : false; 164 } 165 166 private RemoteViews getRemoteViews(Notification notifiction) { 167 Notification.Builder builder = Notification.Builder.recoverBuilder( 168 getBaseContext(), notifiction); 169 if (builder == null) { 170 return null; 171 } 172 173 return builder.createContentView(); 174 } 175 176 private ProgressBar getProgresssBar(RemoteViews remoteViews) { 177 if (remoteViews == null) { 178 return null; 179 } 180 181 View view = remoteViews.apply(getBaseContext(), mFrameLayout); 182 return getProgressBarImpl(view); 183 } 184 185 private ProgressBar getProgressBarImpl(View view) { 186 if (view == null || !(view instanceof ViewGroup)) { 187 return null; 188 } 189 190 ViewGroup viewGroup = (ViewGroup)view; 191 if (viewGroup.getChildCount() <= 0) { 192 return null; 193 } 194 195 ProgressBar result = null; 196 for (int i = 0; i < viewGroup.getChildCount(); i++) { 197 View v = viewGroup.getChildAt(i); 198 if (v instanceof ProgressBar) { 199 result = ((ProgressBar)v); 200 break; 201 } else if (v instanceof ViewGroup) { 202 result = getProgressBarImpl(v); 203 if (result != null) { 204 break; 205 } 206 } 207 } 208 return result; 209 } 210 } 211 212