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