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.quickstep;
18 
19 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
20 
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.res.Resources;
25 import android.util.Log;
26 
27 import com.android.launcher3.touch.PagedOrientationHandler;
28 import com.android.launcher3.util.MainThreadInitializedObject;
29 
30 import java.io.PrintWriter;
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /**
35  * Observer for the resource config that specifies the navigation bar mode.
36  */
37 public class SysUINavigationMode {
38 
39     public enum Mode {
40         THREE_BUTTONS(false, 0),
41         TWO_BUTTONS(true, 1),
42         NO_BUTTON(true, 2);
43 
44         public final boolean hasGestures;
45         public final int resValue;
46 
Mode(boolean hasGestures, int resValue)47         Mode(boolean hasGestures, int resValue) {
48             this.hasGestures = hasGestures;
49             this.resValue = resValue;
50         }
51     }
52 
getMode(Context context)53     public static Mode getMode(Context context) {
54         return INSTANCE.get(context).getMode();
55     }
56 
57     public static final MainThreadInitializedObject<SysUINavigationMode> INSTANCE =
58             new MainThreadInitializedObject<>(SysUINavigationMode::new);
59 
60     private static final String TAG = "SysUINavigationMode";
61 
62     private static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
63     private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
64             "config_navBarInteractionMode";
65 
66     private final Context mContext;
67     private Mode mMode;
68 
69     private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
70 
SysUINavigationMode(Context context)71     public SysUINavigationMode(Context context) {
72         mContext = context;
73         initializeMode();
74 
75         mContext.registerReceiver(new BroadcastReceiver() {
76             @Override
77             public void onReceive(Context context, Intent intent) {
78                 updateMode();
79             }
80         }, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
81     }
82 
83     /** Updates navigation mode when needed. */
updateMode()84     public void updateMode() {
85         Mode oldMode = mMode;
86         initializeMode();
87         if (mMode != oldMode) {
88             dispatchModeChange();
89         }
90     }
91 
initializeMode()92     private void initializeMode() {
93         int modeInt = getSystemIntegerRes(mContext, NAV_BAR_INTERACTION_MODE_RES_NAME);
94         for(Mode m : Mode.values()) {
95             if (m.resValue == modeInt) {
96                 mMode = m;
97             }
98         }
99     }
100 
dispatchModeChange()101     private void dispatchModeChange() {
102         for (NavigationModeChangeListener listener : mChangeListeners) {
103             listener.onNavigationModeChanged(mMode);
104         }
105     }
106 
addModeChangeListener(NavigationModeChangeListener listener)107     public Mode addModeChangeListener(NavigationModeChangeListener listener) {
108         mChangeListeners.add(listener);
109         return mMode;
110     }
111 
removeModeChangeListener(NavigationModeChangeListener listener)112     public void removeModeChangeListener(NavigationModeChangeListener listener) {
113         mChangeListeners.remove(listener);
114     }
115 
getMode()116     public Mode getMode() {
117         return mMode;
118     }
119 
getSystemIntegerRes(Context context, String resName)120     private static int getSystemIntegerRes(Context context, String resName) {
121         Resources res = context.getResources();
122         int resId = res.getIdentifier(resName, "integer", "android");
123 
124         if (resId != 0) {
125             return res.getInteger(resId);
126         } else {
127             Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
128             return -1;
129         }
130     }
131 
132     /** @return Whether we can remove the shelf from overview. */
removeShelfFromOverview(Context context)133     public static boolean removeShelfFromOverview(Context context) {
134         // The shelf is core to the two-button mode model, so we need to continue supporting it.
135         return getMode(context) != Mode.TWO_BUTTONS;
136     }
137 
hideShelfInTwoButtonLandscape(Context context, PagedOrientationHandler pagedOrientationHandler)138     public static boolean hideShelfInTwoButtonLandscape(Context context,
139             PagedOrientationHandler pagedOrientationHandler) {
140         return  getMode(context) == Mode.TWO_BUTTONS &&
141                 !pagedOrientationHandler.isLayoutNaturalToLauncher();
142     }
143 
dump(PrintWriter pw)144     public void dump(PrintWriter pw) {
145         pw.println("SysUINavigationMode:");
146         pw.println("  mode=" + mMode.name());
147     }
148 
149     public interface NavigationModeChangeListener {
150 
onNavigationModeChanged(Mode newMode)151         void onNavigationModeChanged(Mode newMode);
152     }
153 }