1 /* 2 * Copyright (C) 2019 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.car.developeroptions.gestures; 18 19 import static android.os.UserHandle.USER_CURRENT; 20 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; 21 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY; 22 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; 23 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; 24 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; 25 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.om.IOverlayManager; 30 import android.content.pm.PackageManager; 31 import android.os.RemoteException; 32 import android.text.TextUtils; 33 import android.view.View; 34 35 import androidx.preference.Preference; 36 import androidx.preference.PreferenceScreen; 37 38 import com.android.car.developeroptions.R; 39 import com.android.car.developeroptions.widget.RadioButtonPreference; 40 41 public abstract class SystemNavigationPreferenceController extends GesturePreferenceController 42 implements RadioButtonPreference.OnClickListener { 43 44 private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; 45 private static final String PREF_KEY_VIDEO = "gesture_swipe_up_video"; 46 47 private static final String[] RADIO_BUTTONS_IN_GROUP = { 48 SystemNavigationLegacyPreferenceController.PREF_KEY_LEGACY, 49 SystemNavigationSwipeUpPreferenceController.PREF_KEY_SWIPE_UP, 50 SystemNavigationEdgeToEdgePreferenceController.PREF_KEY_EDGE_TO_EDGE, 51 }; 52 53 protected final IOverlayManager mOverlayManager; 54 protected PreferenceScreen mPreferenceScreen; 55 SystemNavigationPreferenceController(Context context, IOverlayManager overlayManager, String key)56 public SystemNavigationPreferenceController(Context context, IOverlayManager overlayManager, 57 String key) { 58 super(context, key); 59 mOverlayManager = overlayManager; 60 } 61 62 @Override getAvailabilityStatus()63 public int getAvailabilityStatus() { 64 return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE; 65 } 66 67 @Override displayPreference(PreferenceScreen screen)68 public void displayPreference(PreferenceScreen screen) { 69 super.displayPreference(screen); 70 mPreferenceScreen = screen; 71 72 Preference preference = screen.findPreference(getPreferenceKey()); 73 if (preference != null && preference instanceof RadioButtonPreference) { 74 RadioButtonPreference radioPreference = (RadioButtonPreference) preference; 75 radioPreference.setOnClickListener(this); 76 radioPreference.setAppendixVisibility(View.GONE); 77 } 78 } 79 80 @Override setChecked(boolean isChecked)81 public boolean setChecked(boolean isChecked) { 82 if (!isChecked || mPreferenceScreen == null) { 83 return false; 84 } 85 Preference preference = mPreferenceScreen.findPreference(getPreferenceKey()); 86 if (preference != null && preference instanceof RadioButtonPreference) { 87 onRadioButtonClicked((RadioButtonPreference) preference); 88 } 89 return true; 90 } 91 92 @Override getSummary()93 public CharSequence getSummary() { 94 if (isEdgeToEdgeEnabled(mContext)) { 95 return mContext.getText(R.string.edge_to_edge_navigation_title); 96 } else if (isSwipeUpEnabled(mContext)) { 97 return mContext.getText(R.string.swipe_up_to_switch_apps_title); 98 } else { 99 return mContext.getText(R.string.legacy_navigation_title); 100 } 101 } 102 103 @Override getVideoPrefKey()104 protected String getVideoPrefKey() { 105 return PREF_KEY_VIDEO; 106 } 107 isGestureAvailable(Context context)108 static boolean isGestureAvailable(Context context) { 109 if (!context.getResources().getBoolean( 110 com.android.internal.R.bool.config_swipe_up_gesture_setting_available)) { 111 return false; 112 } 113 114 final ComponentName recentsComponentName = ComponentName.unflattenFromString( 115 context.getString(com.android.internal.R.string.config_recentsComponentName)); 116 if (recentsComponentName == null) { 117 return false; 118 } 119 final Intent quickStepIntent = new Intent(ACTION_QUICKSTEP) 120 .setPackage(recentsComponentName.getPackageName()); 121 if (context.getPackageManager().resolveService(quickStepIntent, 122 PackageManager.MATCH_SYSTEM_ONLY) == null) { 123 return false; 124 } 125 return true; 126 } 127 selectRadioButtonInGroup(String preferenceKey, PreferenceScreen screen)128 static void selectRadioButtonInGroup(String preferenceKey, PreferenceScreen screen) { 129 if (screen == null) { 130 return; 131 } 132 for (String key : RADIO_BUTTONS_IN_GROUP) { 133 ((RadioButtonPreference) screen.findPreference(key)).setChecked( 134 TextUtils.equals(key, preferenceKey)); 135 } 136 } 137 138 /** 139 * Enables the specified overlay package. 140 */ setNavBarInteractionMode(IOverlayManager overlayManager, String overlayPackage)141 static void setNavBarInteractionMode(IOverlayManager overlayManager, String overlayPackage) { 142 setOverlayEnabled(overlayManager, NAV_BAR_MODE_3BUTTON_OVERLAY, 143 overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY); 144 setOverlayEnabled(overlayManager, NAV_BAR_MODE_2BUTTON_OVERLAY, 145 overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY); 146 setOverlayEnabled(overlayManager, NAV_BAR_MODE_GESTURAL_OVERLAY, 147 overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY); 148 } 149 isSwipeUpEnabled(Context context)150 static boolean isSwipeUpEnabled(Context context) { 151 if (isEdgeToEdgeEnabled(context)) { 152 return false; 153 } 154 return NAV_BAR_MODE_2BUTTON == context.getResources().getInteger( 155 com.android.internal.R.integer.config_navBarInteractionMode); 156 } 157 isEdgeToEdgeEnabled(Context context)158 static boolean isEdgeToEdgeEnabled(Context context) { 159 return NAV_BAR_MODE_GESTURAL == context.getResources().getInteger( 160 com.android.internal.R.integer.config_navBarInteractionMode); 161 } 162 setOverlayEnabled(IOverlayManager overlayManager, String pkg, boolean enabled)163 static void setOverlayEnabled(IOverlayManager overlayManager, String pkg, boolean enabled) { 164 try { 165 overlayManager.setEnabled(pkg, enabled, USER_CURRENT); 166 } catch (RemoteException e) { 167 throw e.rethrowFromSystemServer(); 168 } 169 } 170 } 171