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