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 package com.android.launcher3.states; 17 18 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; 19 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 21 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; 22 23 import static com.android.launcher3.Utilities.dpiFromPx; 24 import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH; 25 26 import android.content.SharedPreferences; 27 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 28 29 import com.android.launcher3.BaseActivity; 30 import com.android.launcher3.DeviceProfile; 31 import com.android.launcher3.Utilities; 32 import com.android.launcher3.util.UiThreadHelper; 33 34 /** 35 * Utility class to manage launcher rotation 36 */ 37 public class RotationHelper implements OnSharedPreferenceChangeListener, 38 DeviceProfile.OnDeviceProfileChangeListener { 39 40 private static final String TAG = "RotationHelper"; 41 42 public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; 43 44 /** 45 * Returns the default value of {@link #ALLOW_ROTATION_PREFERENCE_KEY} preference. 46 */ getAllowRotationDefaultValue(DeviceProfile deviceProfile)47 public static boolean getAllowRotationDefaultValue(DeviceProfile deviceProfile) { 48 // If the device's pixel density was scaled (usually via settings for A11y), use the 49 // original dimensions to determine if rotation is allowed of not. 50 float originalSmallestWidth = dpiFromPx( 51 Math.min(deviceProfile.widthPx, deviceProfile.heightPx), DENSITY_DEVICE_STABLE); 52 return originalSmallestWidth >= MIN_TABLET_WIDTH; 53 } 54 55 public static final int REQUEST_NONE = 0; 56 public static final int REQUEST_ROTATE = 1; 57 public static final int REQUEST_LOCK = 2; 58 59 private BaseActivity mActivity; 60 private SharedPreferences mSharedPrefs = null; 61 62 private boolean mIgnoreAutoRotateSettings; 63 private boolean mForceAllowRotationForTesting; 64 private boolean mHomeRotationEnabled; 65 66 /** 67 * Rotation request made by 68 * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}. 69 * This supersedes any other request. 70 */ 71 private int mStateHandlerRequest = REQUEST_NONE; 72 /** 73 * Rotation request made by an app transition 74 */ 75 private int mCurrentTransitionRequest = REQUEST_NONE; 76 /** 77 * Rotation request made by a Launcher State 78 */ 79 private int mCurrentStateRequest = REQUEST_NONE; 80 81 // This is used to defer setting rotation flags until the activity is being created 82 private boolean mInitialized; 83 private boolean mDestroyed; 84 85 // Initialize mLastActivityFlags to a value not used by SCREEN_ORIENTATION flags 86 private int mLastActivityFlags = -999; 87 RotationHelper(BaseActivity activity)88 public RotationHelper(BaseActivity activity) { 89 mActivity = activity; 90 } 91 setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings)92 private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings) { 93 // On large devices we do not handle auto-rotate differently. 94 mIgnoreAutoRotateSettings = ignoreAutoRotateSettings; 95 if (!mIgnoreAutoRotateSettings) { 96 if (mSharedPrefs == null) { 97 mSharedPrefs = Utilities.getPrefs(mActivity); 98 mSharedPrefs.registerOnSharedPreferenceChangeListener(this); 99 } 100 mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, 101 getAllowRotationDefaultValue(mActivity.getDeviceProfile())); 102 } else { 103 if (mSharedPrefs != null) { 104 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); 105 mSharedPrefs = null; 106 } 107 } 108 } 109 110 @Override onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s)111 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { 112 if (mDestroyed) return; 113 boolean wasRotationEnabled = mHomeRotationEnabled; 114 mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, 115 getAllowRotationDefaultValue(mActivity.getDeviceProfile())); 116 if (mHomeRotationEnabled != wasRotationEnabled) { 117 notifyChange(); 118 } 119 } 120 121 @Override onDeviceProfileChanged(DeviceProfile dp)122 public void onDeviceProfileChanged(DeviceProfile dp) { 123 boolean ignoreAutoRotateSettings = dp.isTablet; 124 if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) { 125 setIgnoreAutoRotateSettings(ignoreAutoRotateSettings); 126 notifyChange(); 127 } 128 } 129 setStateHandlerRequest(int request)130 public void setStateHandlerRequest(int request) { 131 if (mStateHandlerRequest != request) { 132 mStateHandlerRequest = request; 133 notifyChange(); 134 } 135 } 136 setCurrentTransitionRequest(int request)137 public void setCurrentTransitionRequest(int request) { 138 if (mCurrentTransitionRequest != request) { 139 mCurrentTransitionRequest = request; 140 notifyChange(); 141 } 142 } 143 setCurrentStateRequest(int request)144 public void setCurrentStateRequest(int request) { 145 if (mCurrentStateRequest != request) { 146 mCurrentStateRequest = request; 147 notifyChange(); 148 } 149 } 150 151 // Used by tests only. forceAllowRotationForTesting(boolean allowRotation)152 public void forceAllowRotationForTesting(boolean allowRotation) { 153 mForceAllowRotationForTesting = allowRotation; 154 notifyChange(); 155 } 156 initialize()157 public void initialize() { 158 if (!mInitialized) { 159 mInitialized = true; 160 setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().isTablet); 161 mActivity.addOnDeviceProfileChangeListener(this); 162 notifyChange(); 163 } 164 } 165 destroy()166 public void destroy() { 167 if (!mDestroyed) { 168 mDestroyed = true; 169 mActivity.removeOnDeviceProfileChangeListener(this); 170 mActivity = null; 171 if (mSharedPrefs != null) { 172 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); 173 } 174 } 175 } 176 notifyChange()177 private void notifyChange() { 178 if (!mInitialized || mDestroyed) { 179 return; 180 } 181 182 final int activityFlags; 183 if (mStateHandlerRequest != REQUEST_NONE) { 184 activityFlags = mStateHandlerRequest == REQUEST_LOCK ? 185 SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; 186 } else if (mCurrentTransitionRequest != REQUEST_NONE) { 187 activityFlags = mCurrentTransitionRequest == REQUEST_LOCK ? 188 SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; 189 } else if (mCurrentStateRequest == REQUEST_LOCK) { 190 activityFlags = SCREEN_ORIENTATION_LOCKED; 191 } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE 192 || mHomeRotationEnabled || mForceAllowRotationForTesting) { 193 activityFlags = SCREEN_ORIENTATION_UNSPECIFIED; 194 } else { 195 // If auto rotation is off, allow rotation on the activity, in case the user is using 196 // forced rotation. 197 activityFlags = SCREEN_ORIENTATION_NOSENSOR; 198 } 199 if (activityFlags != mLastActivityFlags) { 200 mLastActivityFlags = activityFlags; 201 UiThreadHelper.setOrientationAsync(mActivity, activityFlags); 202 } 203 } 204 205 /** 206 * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. 207 * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... 208 * A value of 0 means no rotation has been applied 209 */ deltaRotation(int oldRotation, int newRotation)210 public static int deltaRotation(int oldRotation, int newRotation) { 211 int delta = newRotation - oldRotation; 212 if (delta < 0) delta += 4; 213 return delta; 214 } 215 216 @Override toString()217 public String toString() { 218 return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d, " 219 + "mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, " 220 + "mHomeRotationEnabled=%b, mForceAllowRotationForTesting=%b]", 221 mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags, 222 mIgnoreAutoRotateSettings, mHomeRotationEnabled, mForceAllowRotationForTesting); 223 } 224 } 225