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