• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.systemui.qs.tiles;
18 
19 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
20 
21 import static com.android.systemui.statusbar.policy.RotationLockControllerImpl.hasSufficientPermission;
22 
23 import android.content.Intent;
24 import android.content.res.Configuration;
25 import android.content.res.Resources;
26 import android.hardware.SensorPrivacyManager;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.provider.Settings;
30 import android.provider.Settings.Secure;
31 import android.service.quicksettings.Tile;
32 import android.widget.Switch;
33 
34 import androidx.annotation.Nullable;
35 
36 import com.android.internal.logging.MetricsLogger;
37 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
38 import com.android.systemui.animation.Expandable;
39 import com.android.systemui.dagger.qualifiers.Background;
40 import com.android.systemui.dagger.qualifiers.Main;
41 import com.android.systemui.plugins.ActivityStarter;
42 import com.android.systemui.plugins.FalsingManager;
43 import com.android.systemui.plugins.qs.QSTile.BooleanState;
44 import com.android.systemui.plugins.statusbar.StatusBarStateController;
45 import com.android.systemui.qs.QSHost;
46 import com.android.systemui.qs.QsEventLogger;
47 import com.android.systemui.qs.UserSettingObserver;
48 import com.android.systemui.qs.logging.QSLogger;
49 import com.android.systemui.qs.tileimpl.QSTileImpl;
50 import com.android.systemui.res.R;
51 import com.android.systemui.statusbar.policy.BatteryController;
52 import com.android.systemui.statusbar.policy.RotationLockController;
53 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
54 import com.android.systemui.util.settings.SecureSettings;
55 
56 import javax.inject.Inject;
57 
58 /** Quick settings tile: Rotation **/
59 public class RotationLockTile extends QSTileImpl<BooleanState> implements
60         BatteryController.BatteryStateChangeCallback {
61 
62     public static final String TILE_SPEC = "rotation";
63 
64     private static final String EMPTY_SECONDARY_STRING = "";
65 
66     private final Icon mIcon =
67             maybeLoadResourceIcon(com.android.internal.R.drawable.ic_qs_auto_rotate);
68     private final RotationLockController mController;
69     private final SensorPrivacyManager mPrivacyManager;
70     private final BatteryController mBatteryController;
71     private final UserSettingObserver mSetting;
72     private final boolean mAllowRotationResolver;
73 
74     @Inject
RotationLockTile( QSHost host, QsEventLogger uiEventLogger, @Background Looper backgroundLooper, @Main Handler mainHandler, FalsingManager falsingManager, MetricsLogger metricsLogger, StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, RotationLockController rotationLockController, SensorPrivacyManager privacyManager, BatteryController batteryController, SecureSettings secureSettings )75     public RotationLockTile(
76             QSHost host,
77             QsEventLogger uiEventLogger,
78             @Background Looper backgroundLooper,
79             @Main Handler mainHandler,
80             FalsingManager falsingManager,
81             MetricsLogger metricsLogger,
82             StatusBarStateController statusBarStateController,
83             ActivityStarter activityStarter,
84             QSLogger qsLogger,
85             RotationLockController rotationLockController,
86             SensorPrivacyManager privacyManager,
87             BatteryController batteryController,
88             SecureSettings secureSettings
89     ) {
90         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
91                 statusBarStateController, activityStarter, qsLogger);
92         mController = rotationLockController;
93         mController.observe(this, mCallback);
94         mPrivacyManager = privacyManager;
95         mBatteryController = batteryController;
96         int currentUser = host.getUserContext().getUserId();
97         mSetting = new UserSettingObserver(
98                 secureSettings,
99                 mHandler,
100                 Secure.CAMERA_AUTOROTATE,
101                 currentUser
102         ) {
103             @Override
104             protected void handleValueChanged(int value, boolean observedChange) {
105                 // mHandler is the background handler so calling this is OK
106                 handleRefreshState(null);
107             }
108         };
109         mBatteryController.observe(getLifecycle(), this);
110         mAllowRotationResolver = mContext.getResources().getBoolean(
111                 com.android.internal.R.bool.config_allowRotationResolver);
112     }
113 
114     @Override
handleInitialize()115     protected void handleInitialize() {
116         mPrivacyManager.addSensorPrivacyListener(CAMERA, mSensorPrivacyChangedListener);
117     }
118 
119     @Override
onPowerSaveChanged(boolean isPowerSave)120     public void onPowerSaveChanged(boolean isPowerSave) {
121         refreshState();
122     }
123 
124     @Override
newTileState()125     public BooleanState newTileState() {
126         return new BooleanState();
127     }
128 
129     @Override
getLongClickIntent()130     public Intent getLongClickIntent() {
131         return new Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS);
132     }
133 
134     @Override
handleClick(@ullable Expandable expandable)135     protected void handleClick(@Nullable Expandable expandable) {
136         final boolean newState = !mState.value;
137         mController.setRotationLocked(!newState, /* caller= */ "RotationLockTile#handleClick");
138         refreshState(newState);
139     }
140 
141     @Override
getTileLabel()142     public CharSequence getTileLabel() {
143         return getState().label;
144     }
145 
146     @Override
handleUpdateState(BooleanState state, Object arg)147     protected void handleUpdateState(BooleanState state, Object arg) {
148         final boolean rotationLocked = mController.isRotationLocked();
149 
150         final boolean powerSave = mBatteryController.isPowerSave();
151         final boolean cameraLocked = mPrivacyManager.isSensorPrivacyEnabled(CAMERA);
152         final boolean cameraRotation = mAllowRotationResolver &&
153                 !powerSave && !cameraLocked && hasSufficientPermission(mContext)
154                         && mController.isCameraRotationEnabled();
155         state.value = !rotationLocked;
156         state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
157         state.icon = maybeLoadResourceIcon(R.drawable.qs_auto_rotate_icon_off);
158         state.contentDescription = getAccessibilityString(rotationLocked);
159         if (!rotationLocked) {
160             state.secondaryLabel = cameraRotation ? mContext.getResources().getString(
161                     R.string.rotation_lock_camera_rotation_on)
162                     : EMPTY_SECONDARY_STRING;
163             state.icon = maybeLoadResourceIcon(R.drawable.qs_auto_rotate_icon_on);
164         } else {
165             state.secondaryLabel = EMPTY_SECONDARY_STRING;
166         }
167         state.stateDescription = state.secondaryLabel;
168 
169         state.expandedAccessibilityClassName = Switch.class.getName();
170         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
171     }
172 
173     @Override
handleDestroy()174     protected void handleDestroy() {
175         super.handleDestroy();
176         mSetting.setListening(false);
177         mPrivacyManager.removeSensorPrivacyListener(CAMERA, mSensorPrivacyChangedListener);
178     }
179 
180     @Override
handleSetListening(boolean listening)181     public void handleSetListening(boolean listening) {
182         super.handleSetListening(listening);
183         mSetting.setListening(listening);
184     }
185 
186     @Override
handleUserSwitch(int newUserId)187     protected void handleUserSwitch(int newUserId) {
188         mSetting.setUserId(newUserId);
189         handleRefreshState(null);
190     }
191 
isCurrentOrientationLockPortrait(RotationLockController controller, Resources resources)192     public static boolean isCurrentOrientationLockPortrait(RotationLockController controller,
193             Resources resources) {
194         int lockOrientation = controller.getRotationLockOrientation();
195         if (lockOrientation == Configuration.ORIENTATION_UNDEFINED) {
196             // Freely rotating device; use current rotation
197             return resources.getConfiguration().orientation
198                     != Configuration.ORIENTATION_LANDSCAPE;
199         } else {
200             return lockOrientation != Configuration.ORIENTATION_LANDSCAPE;
201         }
202     }
203 
204     @Override
getMetricsCategory()205     public int getMetricsCategory() {
206         return MetricsEvent.QS_ROTATIONLOCK;
207     }
208 
209     /**
210      * Get the correct accessibility string based on the state
211      *
212      * @param locked Whether or not rotation is locked.
213      */
getAccessibilityString(boolean locked)214     private String getAccessibilityString(boolean locked) {
215         return mContext.getString(R.string.accessibility_quick_settings_rotation);
216     }
217 
218     private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() {
219         @Override
220         public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
221             refreshState(rotationLocked);
222         }
223     };
224 
225     private final SensorPrivacyManager.OnSensorPrivacyChangedListener
226             mSensorPrivacyChangedListener =
227             (sensor, enabled) -> refreshState();
228 }
229