Home | History | Annotate | Download | only in automatic
      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(15);
     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
     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      */
    124     public static void maybeShowNotification(Context context) {
    125         if (shouldShowNotification(context)) {
    126             showNotification(context);
    127         }
    128     }
    129 
    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 
    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 
    186         NotificationManager manager =
    187                 ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
    188         manager.notify(NOTIFICATION_ID, builder.build());
    189     }
    190 
    191     private void cancelNotification(Context context, Intent intent) {
    192         if (intent.getAction() == INTENT_ACTION_DISMISS) {
    193             incrementNotificationDismissedCount(context);
    194         } else {
    195             incrementNotificationShownCount(context);
    196         }
    197 
    198         int id = intent.getIntExtra(INTENT_EXTRA_ID, -1);
    199         if (id == -1) {
    200             return;
    201         }
    202         NotificationManager manager = (NotificationManager) context
    203                 .getSystemService(Context.NOTIFICATION_SERVICE);
    204         manager.cancel(id);
    205     }
    206 
    207     private void incrementNotificationShownCount(Context context) {
    208         SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME,
    209                 Context.MODE_PRIVATE);
    210         SharedPreferences.Editor editor = sp.edit();
    211         int shownCount = sp.getInt(NotificationController.NOTIFICATION_SHOWN_COUNT, 0) + 1;
    212         editor.putInt(NotificationController.NOTIFICATION_SHOWN_COUNT, shownCount);
    213         editor.apply();
    214     }
    215 
    216     private void incrementNotificationDismissedCount(Context context) {
    217         SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME,
    218                 Context.MODE_PRIVATE);
    219         SharedPreferences.Editor editor = sp.edit();
    220         int dismissCount = sp.getInt(NOTIFICATION_DISMISS_COUNT, 0) + 1;
    221         editor.putInt(NOTIFICATION_DISMISS_COUNT, dismissCount);
    222         editor.apply();
    223     }
    224 
    225     private void delayNextNotification(Context context, long timeInMillis) {
    226         SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME,
    227                 Context.MODE_PRIVATE);
    228         SharedPreferences.Editor editor = sp.edit();
    229         editor.putLong(NOTIFICATION_NEXT_SHOW_TIME,
    230                 System.currentTimeMillis() + timeInMillis);
    231         editor.apply();
    232     }
    233 }
    234