NotificationController.java revision 89dab1a3b1434094cbc70958fb89f4e7f1765a10
1/* 2 * Copyright (C) 2016 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 17package com.android.storagemanager.automatic; 18 19import android.app.Notification; 20import android.app.NotificationManager; 21import android.app.PendingIntent; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.SharedPreferences; 26import android.content.res.Resources; 27import android.os.SystemProperties; 28import android.provider.Settings; 29 30import com.android.storagemanager.R; 31import com.android.storagemanager.automatic.WarningDialogActivity; 32import com.android.storagemanager.overlay.FeatureFactory; 33 34import java.util.concurrent.TimeUnit; 35 36/** 37 * NotificationController handles the responses to the Automatic Storage Management low storage 38 * notification. 39 */ 40public class NotificationController extends BroadcastReceiver { 41 /** 42 * Intent action for if the user taps "Turn on" for the automatic storage manager. 43 */ 44 public static final String INTENT_ACTION_ACTIVATE_ASM = 45 "com.android.storagemanager.automatic.ACTIVATE"; 46 47 /** 48 * Intent action for if the user swipes the notification away. 49 */ 50 public static final String INTENT_ACTION_DISMISS = 51 "com.android.storagemanager.automatic.DISMISS"; 52 53 /** 54 * Intent action for if the user explicitly hits "No thanks" on the notification. 55 */ 56 public static final String INTENT_ACTION_NO_THANKS = 57 "com.android.storagemanager.automatic.NO_THANKS"; 58 59 /** 60 * Intent action to maybe show the ASM upsell notification. 61 */ 62 public static final String INTENT_ACTION_SHOW_NOTIFICATION = 63 "com.android.storagemanager.automatic.show_notification"; 64 65 /** 66 * Intent action for forcefully showing the notification, even if the conditions are not valid. 67 */ 68 private static final String INTENT_ACTION_DEBUG_NOTIFICATION = 69 "com.android.storagemanager.automatic.DEBUG_SHOW_NOTIFICATION"; 70 71 /** 72 * Intent action for if the user taps on the notification. 73 */ 74 private static final String INTENT_ACTION_TAP = 75 "com.android.storagemanager.automatic.SHOW_SETTINGS"; 76 77 /** 78 * Intent extra for the notification id. 79 */ 80 public static final String INTENT_EXTRA_ID = "id"; 81 82 private static final String SHARED_PREFERENCES_NAME = "NotificationController"; 83 private static final String NOTIFICATION_NEXT_SHOW_TIME = "notification_next_show_time"; 84 private static final String NOTIFICATION_SHOWN_COUNT = "notification_shown_count"; 85 private static final String STORAGE_MANAGER_PROPERTY = "ro.storage_manager.enabled"; 86 87 private static final long DISMISS_DELAY = TimeUnit.DAYS.toMillis(15); 88 private static final long NO_THANKS_DELAY = TimeUnit.DAYS.toMillis(90); 89 private static final long MAXIMUM_SHOWN_COUNT = 4; 90 private static final int NOTIFICATION_ID = 0; 91 92 // Keeps the time for test purposes. 93 private Clock mClock; 94 95 @Override 96 public void onReceive(Context context, Intent intent) { 97 switch (intent.getAction()) { 98 case INTENT_ACTION_ACTIVATE_ASM: 99 Settings.Secure.putInt(context.getContentResolver(), 100 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 101 1); 102 // Provide a warning if storage manager is not defaulted on. 103 if (!SystemProperties.getBoolean(STORAGE_MANAGER_PROPERTY, false)) { 104 Intent warningIntent = new Intent(context, WarningDialogActivity.class); 105 context.startActivity(warningIntent); 106 } 107 break; 108 case INTENT_ACTION_NO_THANKS: 109 delayNextNotification(context, NO_THANKS_DELAY); 110 break; 111 case INTENT_ACTION_DISMISS: 112 delayNextNotification(context, DISMISS_DELAY); 113 break; 114 case INTENT_ACTION_SHOW_NOTIFICATION: 115 maybeShowNotification(context); 116 return; 117 case INTENT_ACTION_DEBUG_NOTIFICATION: 118 showNotification(context); 119 return; 120 case INTENT_ACTION_TAP: 121 Intent storageIntent = new Intent(Settings.ACTION_STORAGE_MANAGER_SETTINGS); 122 storageIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 123 context.startActivity(storageIntent); 124 break; 125 } 126 cancelNotification(context, intent); 127 } 128 129 /** 130 * Sets a time provider for the controller. 131 * @param clock The time provider. 132 */ 133 protected void setClock(Clock clock) { 134 mClock = clock; 135 } 136 137 /** 138 * If the conditions for showing the activation notification are met, show the activation 139 * notification. 140 * @param context Context to use for getting resources and to display the notification. 141 */ 142 private void maybeShowNotification(Context context) { 143 if (shouldShowNotification(context)) { 144 showNotification(context); 145 } 146 } 147 148 private boolean shouldShowNotification(Context context) { 149 SharedPreferences sp = context.getSharedPreferences( 150 SHARED_PREFERENCES_NAME, 151 Context.MODE_PRIVATE); 152 int timesShown = sp.getInt(NOTIFICATION_SHOWN_COUNT, 0); 153 if (timesShown >= MAXIMUM_SHOWN_COUNT) { 154 return false; 155 } 156 157 long nextTimeToShow = sp.getLong(NOTIFICATION_NEXT_SHOW_TIME, 0); 158 159 return getCurrentTime() >= nextTimeToShow; 160 } 161 162 private void showNotification(Context context) { 163 Resources res = context.getResources(); 164 Intent noThanksIntent = new Intent(INTENT_ACTION_NO_THANKS); 165 noThanksIntent.putExtra(INTENT_EXTRA_ID, NOTIFICATION_ID); 166 Notification.Action.Builder cancelAction = new Notification.Action.Builder(null, 167 res.getString(R.string.automatic_storage_manager_cancel_button), 168 PendingIntent.getBroadcast(context, 0, noThanksIntent, 169 PendingIntent.FLAG_UPDATE_CURRENT)); 170 171 172 Intent activateIntent = new Intent(INTENT_ACTION_ACTIVATE_ASM); 173 activateIntent.putExtra(INTENT_EXTRA_ID, NOTIFICATION_ID); 174 Notification.Action.Builder activateAutomaticAction = new Notification.Action.Builder(null, 175 res.getString(R.string.automatic_storage_manager_activate_button), 176 PendingIntent.getBroadcast(context, 0, activateIntent, 177 PendingIntent.FLAG_UPDATE_CURRENT)); 178 179 Intent dismissIntent = new Intent(INTENT_ACTION_DISMISS); 180 dismissIntent.putExtra(INTENT_EXTRA_ID, NOTIFICATION_ID); 181 PendingIntent deleteIntent = PendingIntent.getBroadcast(context, 0, 182 new Intent(INTENT_ACTION_DISMISS), 183 PendingIntent.FLAG_ONE_SHOT); 184 185 Intent contentIntent = new Intent(INTENT_ACTION_TAP); 186 contentIntent.putExtra(INTENT_EXTRA_ID, NOTIFICATION_ID); 187 PendingIntent tapIntent = PendingIntent.getBroadcast(context, 0, contentIntent, 188 PendingIntent.FLAG_ONE_SHOT); 189 190 Notification.Builder builder = new Notification.Builder(context) 191 .setSmallIcon(R.drawable.ic_settings_24dp) 192 .setContentTitle( 193 res.getString(R.string.automatic_storage_manager_notification_title)) 194 .setContentText( 195 res.getString(R.string.automatic_storage_manager_notification_summary)) 196 .setStyle(new Notification.BigTextStyle().bigText( 197 res.getString(R.string.automatic_storage_manager_notification_summary))) 198 .addAction(cancelAction.build()) 199 .addAction(activateAutomaticAction.build()) 200 .setContentIntent(tapIntent) 201 .setDeleteIntent(deleteIntent); 202 203 NotificationManager manager = 204 ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)); 205 manager.notify(NOTIFICATION_ID, builder.build()); 206 } 207 208 private void cancelNotification(Context context, Intent intent) { 209 int id = intent.getIntExtra(INTENT_EXTRA_ID, -1); 210 if (id == -1) { 211 return; 212 } 213 NotificationManager manager = (NotificationManager) context 214 .getSystemService(Context.NOTIFICATION_SERVICE); 215 manager.cancel(id); 216 217 incrementNotificationShownCount(context); 218 } 219 220 private void incrementNotificationShownCount(Context context) { 221 SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME, 222 Context.MODE_PRIVATE); 223 SharedPreferences.Editor editor = sp.edit(); 224 int shownCount = sp.getInt(NotificationController.NOTIFICATION_SHOWN_COUNT, 0) + 1; 225 editor.putInt(NotificationController.NOTIFICATION_SHOWN_COUNT, shownCount); 226 editor.apply(); 227 } 228 229 private void delayNextNotification(Context context, long timeInMillis) { 230 SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME, 231 Context.MODE_PRIVATE); 232 SharedPreferences.Editor editor = sp.edit(); 233 editor.putLong(NOTIFICATION_NEXT_SHOW_TIME, 234 getCurrentTime() + timeInMillis); 235 editor.apply(); 236 } 237 238 private long getCurrentTime() { 239 if (mClock == null) { 240 mClock = new Clock(); 241 } 242 243 return mClock.currentTimeMillis(); 244 } 245 246 /** 247 * Clock provides the current time. 248 */ 249 protected static class Clock { 250 /** 251 * Returns the current time in milliseconds. 252 */ 253 public long currentTimeMillis() { 254 return System.currentTimeMillis(); 255 } 256 } 257} 258