1 /* 2 * Copyright (C) 2020 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 package com.android.launcher3.util; 17 18 import android.content.SharedPreferences; 19 import android.util.ArrayMap; 20 21 import androidx.annotation.StringDef; 22 23 import com.android.launcher3.views.ActivityContext; 24 25 import java.lang.annotation.Retention; 26 import java.lang.annotation.RetentionPolicy; 27 import java.util.Collections; 28 import java.util.Map; 29 30 /** 31 * Stores and retrieves onboarding-related data via SharedPreferences. 32 * 33 * @param <T> Context which owns these preferences. 34 */ 35 public class OnboardingPrefs<T extends ActivityContext> { 36 37 public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown"; 38 public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count"; 39 public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count"; 40 public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen"; 41 public static final String SEARCH_KEYBOARD_EDU_SEEN = "launcher.search_edu_seen"; 42 public static final String SEARCH_SNACKBAR_COUNT = "launcher.keyboard_snackbar_count"; 43 public static final String SEARCH_ONBOARDING_COUNT = "launcher.search_onboarding_count"; 44 public static final String TASKBAR_EDU_SEEN = "launcher.taskbar_edu_seen2"; 45 public static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count"; 46 public static final String QSB_SEARCH_ONBOARDING_CARD_DISMISSED = "launcher.qsb_edu_dismiss"; 47 public static final String TASKBAR_EDU_TOOLTIP_STEP = "launcher.taskbar_edu_tooltip_step"; 48 // When adding a new key, add it here as well, to be able to reset it from Developer Options. 49 public static final Map<String, String[]> ALL_PREF_KEYS = Map.of( 50 "All Apps Bounce", new String[] { HOME_BOUNCE_SEEN, HOME_BOUNCE_COUNT }, 51 "Hybrid Hotseat Education", new String[] { HOTSEAT_DISCOVERY_TIP_COUNT, 52 HOTSEAT_LONGPRESS_TIP_SEEN }, 53 "Search Education", new String[] { SEARCH_KEYBOARD_EDU_SEEN, SEARCH_SNACKBAR_COUNT, 54 SEARCH_ONBOARDING_COUNT, QSB_SEARCH_ONBOARDING_CARD_DISMISSED}, 55 "Taskbar Education", new String[] { TASKBAR_EDU_SEEN, TASKBAR_EDU_TOOLTIP_STEP }, 56 "All Apps Visited Count", new String[] {ALL_APPS_VISITED_COUNT} 57 ); 58 59 /** 60 * Events that either have happened or have not (booleans). 61 */ 62 @StringDef(value = { 63 HOME_BOUNCE_SEEN, 64 HOTSEAT_LONGPRESS_TIP_SEEN, 65 SEARCH_KEYBOARD_EDU_SEEN, 66 TASKBAR_EDU_SEEN, 67 QSB_SEARCH_ONBOARDING_CARD_DISMISSED 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 public @interface EventBoolKey {} 71 72 /** 73 * Events that occur multiple times, which we count up to a max defined in {@link #MAX_COUNTS}. 74 */ 75 @StringDef(value = { 76 HOME_BOUNCE_COUNT, 77 HOTSEAT_DISCOVERY_TIP_COUNT, 78 SEARCH_SNACKBAR_COUNT, 79 SEARCH_ONBOARDING_COUNT, 80 ALL_APPS_VISITED_COUNT, 81 TASKBAR_EDU_TOOLTIP_STEP, 82 }) 83 @Retention(RetentionPolicy.SOURCE) 84 public @interface EventCountKey {} 85 86 private static final Map<String, Integer> MAX_COUNTS; 87 88 static { 89 Map<String, Integer> maxCounts = new ArrayMap<>(5); maxCounts.put(HOME_BOUNCE_COUNT, 3)90 maxCounts.put(HOME_BOUNCE_COUNT, 3); maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5)91 maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5); maxCounts.put(SEARCH_SNACKBAR_COUNT, 3)92 maxCounts.put(SEARCH_SNACKBAR_COUNT, 3); 93 // This is the sum of all onboarding cards. Currently there is only 1 card shown 3 times. maxCounts.put(SEARCH_ONBOARDING_COUNT, 3)94 maxCounts.put(SEARCH_ONBOARDING_COUNT, 3); maxCounts.put(ALL_APPS_VISITED_COUNT, 20)95 maxCounts.put(ALL_APPS_VISITED_COUNT, 20); maxCounts.put(TASKBAR_EDU_TOOLTIP_STEP, 2)96 maxCounts.put(TASKBAR_EDU_TOOLTIP_STEP, 2); 97 MAX_COUNTS = Collections.unmodifiableMap(maxCounts); 98 } 99 100 protected final T mLauncher; 101 protected final SharedPreferences mSharedPrefs; 102 OnboardingPrefs(T launcher, SharedPreferences sharedPrefs)103 public OnboardingPrefs(T launcher, SharedPreferences sharedPrefs) { 104 mLauncher = launcher; 105 mSharedPrefs = sharedPrefs; 106 } 107 108 /** @return The number of times we have seen the given event. */ getCount(@ventCountKey String key)109 public int getCount(@EventCountKey String key) { 110 return mSharedPrefs.getInt(key, 0); 111 } 112 113 /** @return Whether we have seen this event enough times, as defined by {@link #MAX_COUNTS}. */ hasReachedMaxCount(@ventCountKey String eventKey)114 public boolean hasReachedMaxCount(@EventCountKey String eventKey) { 115 return hasReachedMaxCount(getCount(eventKey), eventKey); 116 } 117 hasReachedMaxCount(int count, @EventCountKey String eventKey)118 private boolean hasReachedMaxCount(int count, @EventCountKey String eventKey) { 119 return count >= MAX_COUNTS.get(eventKey); 120 } 121 122 /** @return Whether we have seen the given event. */ getBoolean(@ventBoolKey String key)123 public boolean getBoolean(@EventBoolKey String key) { 124 return mSharedPrefs.getBoolean(key, false); 125 } 126 127 /** 128 * Marks on-boarding preference boolean at true 129 */ markChecked(String flag)130 public void markChecked(String flag) { 131 mSharedPrefs.edit().putBoolean(flag, true).apply(); 132 } 133 134 /** 135 * Add 1 to the given event count, if we haven't already reached the max count. 136 * 137 * @return Whether we have now reached the max count. 138 */ incrementEventCount(@ventCountKey String eventKey)139 public boolean incrementEventCount(@EventCountKey String eventKey) { 140 int count = getCount(eventKey); 141 if (hasReachedMaxCount(count, eventKey)) { 142 return true; 143 } 144 count++; 145 mSharedPrefs.edit().putInt(eventKey, count).apply(); 146 return hasReachedMaxCount(count, eventKey); 147 } 148 149 /** 150 * Sets the event count to the given value. 151 * 152 * @return Whether we have now reached the max count. 153 */ setEventCount(int count, @EventCountKey String eventKey)154 public boolean setEventCount(int count, @EventCountKey String eventKey) { 155 mSharedPrefs.edit().putInt(eventKey, count).apply(); 156 return hasReachedMaxCount(count, eventKey); 157 } 158 } 159