1 /* 2 * Copyright (C) 2018 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.gestures; 18 19 import static android.os.UserHandle.USER_CURRENT; 20 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; 21 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; 22 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; 23 24 import android.app.settings.SettingsEnums; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.SharedPreferences; 28 import android.content.om.IOverlayManager; 29 import android.content.om.OverlayInfo; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.provider.Settings; 33 34 import androidx.annotation.VisibleForTesting; 35 import androidx.preference.PreferenceScreen; 36 37 import com.android.settings.R; 38 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; 39 import com.android.settings.overlay.FeatureFactory; 40 import com.android.settings.search.BaseSearchIndexProvider; 41 import com.android.settings.support.actionbar.HelpResourceProvider; 42 import com.android.settings.utils.CandidateInfoExtra; 43 import com.android.settings.widget.RadioButtonPickerFragment; 44 import com.android.settingslib.search.SearchIndexable; 45 import com.android.settingslib.widget.CandidateInfo; 46 import com.android.settingslib.widget.IllustrationPreference; 47 import com.android.settingslib.widget.RadioButtonPreference; 48 49 import java.util.ArrayList; 50 import java.util.List; 51 52 @SearchIndexable 53 public class SystemNavigationGestureSettings extends RadioButtonPickerFragment implements 54 HelpResourceProvider { 55 56 private static final String TAG = "SystemNavigationGesture"; 57 58 @VisibleForTesting 59 static final String KEY_SYSTEM_NAV_3BUTTONS = "system_nav_3buttons"; 60 @VisibleForTesting 61 static final String KEY_SYSTEM_NAV_2BUTTONS = "system_nav_2buttons"; 62 @VisibleForTesting 63 static final String KEY_SYSTEM_NAV_GESTURAL = "system_nav_gestural"; 64 65 public static final String PREF_KEY_SUGGESTION_COMPLETE = 66 "pref_system_navigation_suggestion_complete"; 67 68 private IOverlayManager mOverlayManager; 69 70 private IllustrationPreference mVideoPreference; 71 72 @Override onAttach(Context context)73 public void onAttach(Context context) { 74 super.onAttach(context); 75 76 SuggestionFeatureProvider suggestionFeatureProvider = FeatureFactory.getFactory(context) 77 .getSuggestionFeatureProvider(context); 78 SharedPreferences prefs = suggestionFeatureProvider.getSharedPrefs(context); 79 prefs.edit().putBoolean(PREF_KEY_SUGGESTION_COMPLETE, true).apply(); 80 81 mOverlayManager = IOverlayManager.Stub.asInterface( 82 ServiceManager.getService(Context.OVERLAY_SERVICE)); 83 84 mVideoPreference = new IllustrationPreference(context); 85 setIllustrationVideo(mVideoPreference, getDefaultKey()); 86 87 migrateOverlaySensitivityToSettings(context, mOverlayManager); 88 } 89 90 @Override getMetricsCategory()91 public int getMetricsCategory() { 92 return SettingsEnums.SETTINGS_GESTURE_SWIPE_UP; 93 } 94 95 @Override updateCandidates()96 public void updateCandidates() { 97 final String defaultKey = getDefaultKey(); 98 final String systemDefaultKey = getSystemDefaultKey(); 99 final PreferenceScreen screen = getPreferenceScreen(); 100 screen.removeAll(); 101 screen.addPreference(mVideoPreference); 102 103 final List<? extends CandidateInfo> candidateList = getCandidates(); 104 if (candidateList == null) { 105 return; 106 } 107 for (CandidateInfo info : candidateList) { 108 RadioButtonPreference pref = 109 new RadioButtonPreference(getPrefContext()); 110 bindPreference(pref, info.getKey(), info, defaultKey); 111 bindPreferenceExtra(pref, info.getKey(), info, defaultKey, systemDefaultKey); 112 screen.addPreference(pref); 113 } 114 mayCheckOnlyRadioButton(); 115 } 116 117 @Override bindPreferenceExtra(RadioButtonPreference pref, String key, CandidateInfo info, String defaultKey, String systemDefaultKey)118 public void bindPreferenceExtra(RadioButtonPreference pref, 119 String key, CandidateInfo info, String defaultKey, String systemDefaultKey) { 120 if (!(info instanceof CandidateInfoExtra)) { 121 return; 122 } 123 124 pref.setSummary(((CandidateInfoExtra) info).loadSummary()); 125 126 if (info.getKey() == KEY_SYSTEM_NAV_GESTURAL) { 127 pref.setExtraWidgetOnClickListener((v) -> startActivity(new Intent( 128 GestureNavigationSettingsFragment.GESTURE_NAVIGATION_SETTINGS))); 129 } 130 } 131 132 @Override getPreferenceScreenResId()133 protected int getPreferenceScreenResId() { 134 return R.xml.system_navigation_gesture_settings; 135 } 136 137 @Override getCandidates()138 protected List<? extends CandidateInfo> getCandidates() { 139 final Context c = getContext(); 140 List<CandidateInfoExtra> candidates = new ArrayList<>(); 141 142 if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c, 143 NAV_BAR_MODE_GESTURAL_OVERLAY)) { 144 candidates.add(new CandidateInfoExtra( 145 c.getText(R.string.edge_to_edge_navigation_title), 146 c.getText(R.string.edge_to_edge_navigation_summary), 147 KEY_SYSTEM_NAV_GESTURAL, true /* enabled */)); 148 } 149 if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c, 150 NAV_BAR_MODE_2BUTTON_OVERLAY)) { 151 candidates.add(new CandidateInfoExtra( 152 c.getText(R.string.swipe_up_to_switch_apps_title), 153 c.getText(R.string.swipe_up_to_switch_apps_summary), 154 KEY_SYSTEM_NAV_2BUTTONS, true /* enabled */)); 155 } 156 if (SystemNavigationPreferenceController.isOverlayPackageAvailable(c, 157 NAV_BAR_MODE_3BUTTON_OVERLAY)) { 158 candidates.add(new CandidateInfoExtra( 159 c.getText(R.string.legacy_navigation_title), 160 c.getText(R.string.legacy_navigation_summary), 161 KEY_SYSTEM_NAV_3BUTTONS, true /* enabled */)); 162 } 163 164 return candidates; 165 } 166 167 @Override getDefaultKey()168 protected String getDefaultKey() { 169 return getCurrentSystemNavigationMode(getContext()); 170 } 171 172 @Override setDefaultKey(String key)173 protected boolean setDefaultKey(String key) { 174 setCurrentSystemNavigationMode(mOverlayManager, key); 175 setIllustrationVideo(mVideoPreference, key); 176 177 return true; 178 } 179 migrateOverlaySensitivityToSettings(Context context, IOverlayManager overlayManager)180 static void migrateOverlaySensitivityToSettings(Context context, 181 IOverlayManager overlayManager) { 182 if (!SystemNavigationPreferenceController.isGestureNavigationEnabled(context)) { 183 return; 184 } 185 186 OverlayInfo info = null; 187 try { 188 info = overlayManager.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT); 189 } catch (RemoteException e) { /* Do nothing */ } 190 if (info != null && !info.isEnabled()) { 191 // Enable the default gesture nav overlay. Back sensitivity for left and right are 192 // stored as separate settings values, and other gesture nav overlays are deprecated. 193 setCurrentSystemNavigationMode(overlayManager, KEY_SYSTEM_NAV_GESTURAL); 194 Settings.Secure.putFloat(context.getContentResolver(), 195 Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT, 1.0f); 196 Settings.Secure.putFloat(context.getContentResolver(), 197 Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT, 1.0f); 198 } 199 } 200 201 @VisibleForTesting getCurrentSystemNavigationMode(Context context)202 static String getCurrentSystemNavigationMode(Context context) { 203 if (SystemNavigationPreferenceController.isGestureNavigationEnabled(context)) { 204 return KEY_SYSTEM_NAV_GESTURAL; 205 } else if (SystemNavigationPreferenceController.is2ButtonNavigationEnabled(context)) { 206 return KEY_SYSTEM_NAV_2BUTTONS; 207 } else { 208 return KEY_SYSTEM_NAV_3BUTTONS; 209 } 210 } 211 212 @VisibleForTesting setCurrentSystemNavigationMode(IOverlayManager overlayManager, String key)213 static void setCurrentSystemNavigationMode(IOverlayManager overlayManager, String key) { 214 String overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY; 215 switch (key) { 216 case KEY_SYSTEM_NAV_GESTURAL: 217 overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY; 218 break; 219 case KEY_SYSTEM_NAV_2BUTTONS: 220 overlayPackage = NAV_BAR_MODE_2BUTTON_OVERLAY; 221 break; 222 case KEY_SYSTEM_NAV_3BUTTONS: 223 overlayPackage = NAV_BAR_MODE_3BUTTON_OVERLAY; 224 break; 225 } 226 227 try { 228 overlayManager.setEnabledExclusiveInCategory(overlayPackage, USER_CURRENT); 229 } catch (RemoteException e) { 230 throw e.rethrowFromSystemServer(); 231 } 232 } 233 setIllustrationVideo(IllustrationPreference videoPref, String systemNavKey)234 private static void setIllustrationVideo(IllustrationPreference videoPref, 235 String systemNavKey) { 236 switch (systemNavKey) { 237 case KEY_SYSTEM_NAV_GESTURAL: 238 videoPref.setLottieAnimationResId(R.raw.lottie_system_nav_fully_gestural); 239 break; 240 case KEY_SYSTEM_NAV_2BUTTONS: 241 videoPref.setLottieAnimationResId(R.raw.lottie_system_nav_2_button); 242 break; 243 case KEY_SYSTEM_NAV_3BUTTONS: 244 videoPref.setLottieAnimationResId(R.raw.lottie_system_nav_3_button); 245 break; 246 } 247 } 248 249 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 250 new BaseSearchIndexProvider(R.xml.system_navigation_gesture_settings) { 251 252 @Override 253 protected boolean isPageSearchEnabled(Context context) { 254 return SystemNavigationPreferenceController.isGestureAvailable(context); 255 } 256 }; 257 258 // From HelpResourceProvider 259 @Override getHelpResource()260 public int getHelpResource() { 261 // TODO(b/146001201): Replace with system navigation help page when ready. 262 return R.string.help_uri_default; 263 } 264 } 265