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 17 package com.android.settings.intelligence.suggestions.eligibility; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.content.pm.ResolveInfo; 22 import androidx.annotation.VisibleForTesting; 23 import android.text.format.DateUtils; 24 import android.util.Log; 25 26 import com.android.settings.intelligence.overlay.FeatureFactory; 27 import com.android.settings.intelligence.suggestions.SuggestionFeatureProvider; 28 29 public class DismissedChecker { 30 31 /** 32 * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed. 33 * For instance: 34 * 0,10 35 * Will appear immediately, the 10 is ignored. 36 * 37 * 10 38 * Will appear after 10 days 39 */ 40 @VisibleForTesting 41 static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss"; 42 43 // Shared prefs keys for storing dismissed state. 44 // Index into current dismissed state. 45 @VisibleForTesting 46 static final String SETUP_TIME = "_setup_time"; 47 // Default dismiss rule for suggestions. 48 49 private static final int DEFAULT_FIRST_APPEAR_DAY = 0; 50 51 private static final String TAG = "DismissedChecker"; 52 isEligible(Context context, String id, ResolveInfo info, boolean ignoreAppearRule)53 public static boolean isEligible(Context context, String id, ResolveInfo info, 54 boolean ignoreAppearRule) { 55 final SuggestionFeatureProvider featureProvider = FeatureFactory.get(context) 56 .suggestionFeatureProvider(); 57 final SharedPreferences prefs = featureProvider.getSharedPrefs(context); 58 final long currentTimeMs = System.currentTimeMillis(); 59 final String keySetupTime = id + SETUP_TIME; 60 if (!prefs.contains(keySetupTime)) { 61 prefs.edit() 62 .putLong(keySetupTime, currentTimeMs) 63 .apply(); 64 } 65 66 // Check if it's already manually dismissed 67 final boolean isDismissed = featureProvider.isSuggestionDismissed(context, id); 68 if (isDismissed) { 69 return false; 70 } 71 72 // Parse when suggestion should first appear. Hide suggestion before then. 73 int firstAppearDay = ignoreAppearRule 74 ? DEFAULT_FIRST_APPEAR_DAY 75 : parseAppearDay(info); 76 long setupTime = prefs.getLong(keySetupTime, 0); 77 if (setupTime > currentTimeMs) { 78 // SetupTime is the future, user's date/time is probably wrong at some point. 79 // Force setupTime to be now. So we get a more reasonable firstAppearDay. 80 setupTime = currentTimeMs; 81 } 82 final long firstAppearDayInMs = getFirstAppearTimeMillis(setupTime, firstAppearDay); 83 if (currentTimeMs >= firstAppearDayInMs) { 84 // Dismiss timeout has passed, undismiss it. 85 featureProvider.markSuggestionNotDismissed(context, id); 86 return true; 87 } 88 return false; 89 } 90 91 /** 92 * Parse the first int from a string formatted as "0,1,2..." 93 * The value means suggestion should first appear on Day X. 94 */ parseAppearDay(ResolveInfo info)95 private static int parseAppearDay(ResolveInfo info) { 96 if (!info.activityInfo.metaData.containsKey(META_DATA_DISMISS_CONTROL)) { 97 return 0; 98 } 99 100 final Object firstAppearRule = info.activityInfo.metaData 101 .get(META_DATA_DISMISS_CONTROL); 102 if (firstAppearRule instanceof Integer) { 103 return (int) firstAppearRule; 104 } else { 105 try { 106 final String[] days = ((String) firstAppearRule).split(","); 107 return Integer.parseInt(days[0]); 108 } catch (Exception e) { 109 Log.w(TAG, "Failed to parse appear/dismiss rule, fall back to 0"); 110 return 0; 111 } 112 } 113 } 114 getFirstAppearTimeMillis(long setupTime, int daysDelay)115 private static long getFirstAppearTimeMillis(long setupTime, int daysDelay) { 116 long days = daysDelay * DateUtils.DAY_IN_MILLIS; 117 return setupTime + days; 118 } 119 } 120