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 android.content.SharedPreferences; 24 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 25 import android.content.res.Resources; 26 import android.view.WindowManager; 27 import android.view.WindowManager.LayoutParams; 28 29 import com.android.launcher3.Launcher; 30 import com.android.launcher3.R; 31 import com.android.launcher3.Utilities; 32 import com.android.launcher3.config.FeatureFlags; 33 import com.android.launcher3.util.UiThreadHelper; 34 35 /** 36 * Utility class to manage launcher rotation 37 */ 38 public class RotationHelper implements OnSharedPreferenceChangeListener { 39 40 public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; 41 getAllowRotationDefaultValue()42 public static boolean getAllowRotationDefaultValue() { 43 // If the device was scaled, used the original dimensions to determine if rotation 44 // is allowed of not. 45 Resources res = Resources.getSystem(); 46 int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp 47 * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE; 48 return originalSmallestWidth >= 600; 49 } 50 51 public static final int REQUEST_NONE = 0; 52 public static final int REQUEST_ROTATE = 1; 53 public static final int REQUEST_LOCK = 2; 54 55 private final Launcher mLauncher; 56 private final SharedPreferences mPrefs; 57 58 private boolean mIgnoreAutoRotateSettings; 59 private boolean mAutoRotateEnabled; 60 61 /** 62 * Rotation request made by {@link InternalStateHandler}. This supersedes any other request. 63 */ 64 private int mStateHandlerRequest = REQUEST_NONE; 65 /** 66 * Rotation request made by an app transition 67 */ 68 private int mCurrentTransitionRequest = REQUEST_NONE; 69 /** 70 * Rotation request made by a Launcher State 71 */ 72 private int mCurrentStateRequest = REQUEST_NONE; 73 74 // This is used to defer setting rotation flags until the activity is being created 75 private boolean mInitialized; 76 private boolean mDestroyed; 77 private boolean mRotationHasDifferentUI; 78 79 private int mLastActivityFlags = -1; 80 RotationHelper(Launcher launcher)81 public RotationHelper(Launcher launcher) { 82 mLauncher = launcher; 83 84 // On large devices we do not handle auto-rotate differently. 85 mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation); 86 if (!mIgnoreAutoRotateSettings) { 87 mPrefs = Utilities.getPrefs(mLauncher); 88 mPrefs.registerOnSharedPreferenceChangeListener(this); 89 mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, 90 getAllowRotationDefaultValue()); 91 } else { 92 mPrefs = null; 93 } 94 } 95 setRotationHadDifferentUI(boolean rotationHasDifferentUI)96 public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) { 97 mRotationHasDifferentUI = rotationHasDifferentUI; 98 } 99 homeScreenCanRotate()100 public boolean homeScreenCanRotate() { 101 return mRotationHasDifferentUI || mIgnoreAutoRotateSettings || mAutoRotateEnabled 102 || mStateHandlerRequest != REQUEST_NONE 103 || mLauncher.getDeviceProfile().isMultiWindowMode; 104 } 105 updateRotationAnimation()106 public void updateRotationAnimation() { 107 if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) { 108 WindowManager.LayoutParams lp = mLauncher.getWindow().getAttributes(); 109 int oldAnim = lp.rotationAnimation; 110 lp.rotationAnimation = homeScreenCanRotate() 111 ? WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE 112 : WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; 113 if (oldAnim != lp.rotationAnimation) { 114 mLauncher.getWindow().setAttributes(lp); 115 } 116 } 117 } 118 119 @Override onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s)120 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { 121 boolean wasRotationEnabled = mAutoRotateEnabled; 122 mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, 123 getAllowRotationDefaultValue()); 124 if (mAutoRotateEnabled != wasRotationEnabled) { 125 126 notifyChange(); 127 updateRotationAnimation(); 128 mLauncher.reapplyUi(); 129 } 130 } 131 setStateHandlerRequest(int request)132 public void setStateHandlerRequest(int request) { 133 if (mStateHandlerRequest != request) { 134 mStateHandlerRequest = request; 135 updateRotationAnimation(); 136 notifyChange(); 137 } 138 } 139 setCurrentTransitionRequest(int request)140 public void setCurrentTransitionRequest(int request) { 141 if (mCurrentTransitionRequest != request) { 142 mCurrentTransitionRequest = request; 143 notifyChange(); 144 } 145 } 146 setCurrentStateRequest(int request)147 public void setCurrentStateRequest(int request) { 148 if (mCurrentStateRequest != request) { 149 mCurrentStateRequest = request; 150 notifyChange(); 151 } 152 } 153 154 // Used by tests only. forceAllowRotationForTesting(boolean allowRotation)155 public void forceAllowRotationForTesting(boolean allowRotation) { 156 mIgnoreAutoRotateSettings = 157 allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation); 158 notifyChange(); 159 } 160 initialize()161 public void initialize() { 162 if (!mInitialized) { 163 mInitialized = true; 164 notifyChange(); 165 updateRotationAnimation(); 166 } 167 } 168 destroy()169 public void destroy() { 170 if (!mDestroyed) { 171 mDestroyed = true; 172 if (mPrefs != null) { 173 mPrefs.unregisterOnSharedPreferenceChangeListener(this); 174 } 175 } 176 } 177 notifyChange()178 private void notifyChange() { 179 if (!mInitialized || mDestroyed) { 180 return; 181 } 182 183 final int activityFlags; 184 if (mStateHandlerRequest != REQUEST_NONE) { 185 activityFlags = mStateHandlerRequest == REQUEST_LOCK ? 186 SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; 187 } else if (mCurrentTransitionRequest != REQUEST_NONE) { 188 activityFlags = mCurrentTransitionRequest == REQUEST_LOCK ? 189 SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; 190 } else if (mCurrentStateRequest == REQUEST_LOCK) { 191 activityFlags = SCREEN_ORIENTATION_LOCKED; 192 } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE 193 || mAutoRotateEnabled) { 194 activityFlags = SCREEN_ORIENTATION_UNSPECIFIED; 195 } else { 196 // If auto rotation is off, allow rotation on the activity, in case the user is using 197 // forced rotation. 198 activityFlags = SCREEN_ORIENTATION_NOSENSOR; 199 } 200 if (activityFlags != mLastActivityFlags) { 201 mLastActivityFlags = activityFlags; 202 UiThreadHelper.setOrientationAsync(mLauncher, activityFlags); 203 } 204 } 205 206 @Override toString()207 public String toString() { 208 return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d," 209 + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mAutoRotateEnabled=%b]", 210 mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags, 211 mIgnoreAutoRotateSettings, mAutoRotateEnabled); 212 } 213 } 214