1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.storagemanager.deletionhelper; 16 17 import android.app.Activity; 18 import android.app.AlertDialog; 19 import android.app.Dialog; 20 import android.app.DialogFragment; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.SharedPreferences; 24 import android.os.Bundle; 25 import android.provider.Settings; 26 import android.support.annotation.VisibleForTesting; 27 import android.text.format.Formatter; 28 import com.android.storagemanager.R; 29 30 import java.util.concurrent.TimeUnit; 31 32 /** 33 * Fragment for activating the storage manager after a manual clear. 34 */ 35 public class StorageManagerUpsellDialog extends DialogFragment 36 implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener { 37 public static final String TAG = "StorageManagerUpsellDialog"; 38 private static final String SHARED_PREFERENCES_NAME = "StorageManagerUpsellDialog"; 39 private static final String NEXT_SHOW_TIME = "next_show_time"; 40 private static final String DISMISSED_COUNT = "dismissed_count"; 41 private static final String NO_THANKS_COUNT = "no_thanks_count"; 42 43 private static final String ARGS_FREED_BYTES = "freed_bytes"; 44 45 private static final long NEVER = -1; 46 private static final long DISMISS_SHORT_DELAY = TimeUnit.DAYS.toMillis(14); 47 private static final long DISMISS_LONG_DELAY = TimeUnit.DAYS.toMillis(90); 48 private static final int DISMISS_LONG_THRESHOLD = 9; 49 private static final long NO_THANKS_SHORT_DELAY = TimeUnit.DAYS.toMillis(90); 50 private static final long NO_THANKS_LONG_DELAY = NEVER; 51 private static final int NO_THANKS_LONG_THRESHOLD = 3; 52 53 private Clock mClock; 54 55 public static StorageManagerUpsellDialog newInstance(long freedBytes) { 56 StorageManagerUpsellDialog dialog = new StorageManagerUpsellDialog(); 57 Bundle args = new Bundle(1); 58 args.putLong(ARGS_FREED_BYTES, freedBytes); 59 dialog.setArguments(args); 60 return dialog; 61 } 62 63 @VisibleForTesting(otherwise = VisibleForTesting.NONE) 64 protected void setClock(Clock clock) { 65 mClock = clock; 66 } 67 68 @Override 69 public Dialog onCreateDialog(Bundle savedInstanceState) { 70 final Bundle args = getArguments(); 71 long freedBytes = args.getLong(ARGS_FREED_BYTES); 72 73 final Context context = getContext(); 74 return new AlertDialog.Builder(context) 75 .setTitle(context.getString(R.string.deletion_helper_upsell_title)) 76 .setMessage(context.getString(R.string.deletion_helper_upsell_summary, 77 Formatter.formatFileSize(context, freedBytes))) 78 .setPositiveButton(R.string.deletion_helper_upsell_activate, this) 79 .setNegativeButton(R.string.deletion_helper_upsell_cancel, this) 80 .create(); 81 } 82 83 @Override 84 public void onClick(DialogInterface dialog, int buttonId) { 85 if (buttonId == DialogInterface.BUTTON_POSITIVE) { 86 Settings.Secure.putInt(getActivity().getContentResolver(), 87 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 1); 88 } else { 89 SharedPreferences sp = getSharedPreferences(getContext()); 90 int noThanksCount = sp.getInt(NO_THANKS_COUNT, 0) + 1; 91 SharedPreferences.Editor editor = sp.edit(); 92 editor.putInt(NO_THANKS_COUNT, noThanksCount); 93 long noThanksDelay = getNoThanksDelay(noThanksCount); 94 long nextShowTime = noThanksDelay == NEVER ? NEVER : getCurrentTime() + noThanksDelay; 95 editor.putLong(NEXT_SHOW_TIME, nextShowTime); 96 editor.apply(); 97 } 98 99 finishActivity(); 100 } 101 102 @Override 103 public void onCancel(DialogInterface dialog) { 104 SharedPreferences sp = getSharedPreferences(getContext()); 105 int dismissCount = sp.getInt(DISMISSED_COUNT, 0) + 1; 106 SharedPreferences.Editor editor = sp.edit(); 107 editor.putInt(DISMISSED_COUNT, dismissCount); 108 editor.putLong(NEXT_SHOW_TIME, getCurrentTime() + getDismissDelay(dismissCount)); 109 editor.apply(); 110 111 finishActivity(); 112 } 113 114 /** 115 * Returns if the dialog should be shown, given the delays between when it is shown. 116 * @param context Context to get shared preferences for determining the next show time. 117 * @param time The current time in millis. 118 */ 119 public static boolean shouldShow(Context context, long time) { 120 boolean isEnabled = 121 Settings.Secure.getInt(context.getContentResolver(), 122 Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED, 0) != 0; 123 if (isEnabled) { 124 return false; 125 } 126 127 long nextTimeToShow = getSharedPreferences(context).getLong(NEXT_SHOW_TIME, 0); 128 if (nextTimeToShow == NEVER) { 129 return false; 130 } 131 132 return time >= nextTimeToShow; 133 } 134 135 private static SharedPreferences getSharedPreferences(Context context) { 136 return context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); 137 } 138 139 private static long getNoThanksDelay(int noThanksCount) { 140 return (noThanksCount > NO_THANKS_LONG_THRESHOLD) 141 ? NO_THANKS_LONG_DELAY : NO_THANKS_SHORT_DELAY; 142 } 143 144 private static long getDismissDelay(int dismissCount) { 145 return (dismissCount > DISMISS_LONG_THRESHOLD) 146 ? DISMISS_LONG_DELAY : DISMISS_SHORT_DELAY; 147 } 148 149 private void finishActivity() { 150 Activity activity = getActivity(); 151 if (activity != null) { 152 activity.finish(); 153 } 154 } 155 156 private long getCurrentTime() { 157 if (mClock == null) { 158 mClock = new Clock(); 159 } 160 161 return mClock.currentTimeMillis(); 162 } 163 164 /** 165 * Clock provides the current time. 166 */ 167 protected static class Clock { 168 /** 169 * Returns the current time in milliseconds. 170 */ 171 public long currentTimeMillis() { 172 return System.currentTimeMillis(); 173 } 174 } 175 } 176