/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.states; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; import android.app.Activity; import android.content.ContentResolver; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.provider.Settings; import android.util.Log; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.UiThreadHelper; /** * Utility class to manage launcher rotation */ public class RotationHelper implements OnSharedPreferenceChangeListener { private static final String TAG = "RotationHelper"; public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; private final ContentResolver mContentResolver; private boolean mSystemAutoRotateEnabled; private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { updateAutoRotateSetting(); } }; public static boolean getAllowRotationDefaultValue() { // If the device's pixel density was scaled (usually via settings for A11y), use the // original dimensions to determine if rotation is allowed of not. Resources res = Resources.getSystem(); int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE; return originalSmallestWidth >= 600; } public static final int REQUEST_NONE = 0; public static final int REQUEST_ROTATE = 1; public static final int REQUEST_LOCK = 2; private final Activity mActivity; private final SharedPreferences mSharedPrefs; private boolean mIgnoreAutoRotateSettings; private boolean mHomeRotationEnabled; /** * Rotation request made by * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}. * This supersedes any other request. */ private int mStateHandlerRequest = REQUEST_NONE; /** * Rotation request made by an app transition */ private int mCurrentTransitionRequest = REQUEST_NONE; /** * Rotation request made by a Launcher State */ private int mCurrentStateRequest = REQUEST_NONE; // This is used to defer setting rotation flags until the activity is being created private boolean mInitialized; private boolean mDestroyed; private int mLastActivityFlags = -1; public RotationHelper(Activity activity) { mActivity = activity; // On large devices we do not handle auto-rotate differently. mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation); if (!mIgnoreAutoRotateSettings) { mSharedPrefs = Utilities.getPrefs(mActivity); mSharedPrefs.registerOnSharedPreferenceChangeListener(this); mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); } else { mSharedPrefs = null; } mContentResolver = activity.getContentResolver(); } private void updateAutoRotateSetting() { int autoRotateEnabled = 0; try { autoRotateEnabled = Settings.System.getInt(mContentResolver, Settings.System.ACCELEROMETER_ROTATION); } catch (Settings.SettingNotFoundException e) { Log.e(TAG, "autorotate setting not found", e); } mSystemAutoRotateEnabled = autoRotateEnabled == 1; } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { boolean wasRotationEnabled = mHomeRotationEnabled; mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); if (mHomeRotationEnabled != wasRotationEnabled) { notifyChange(); updateAutoRotateSetting(); } } public void setStateHandlerRequest(int request) { if (mStateHandlerRequest != request) { mStateHandlerRequest = request; notifyChange(); } } public void setCurrentTransitionRequest(int request) { if (mCurrentTransitionRequest != request) { mCurrentTransitionRequest = request; notifyChange(); } } public void setCurrentStateRequest(int request) { if (mCurrentStateRequest != request) { mCurrentStateRequest = request; notifyChange(); } } // Used by tests only. public void forceAllowRotationForTesting(boolean allowRotation) { mIgnoreAutoRotateSettings = allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation); notifyChange(); } public void initialize() { if (!mInitialized) { mInitialized = true; notifyChange(); mContentResolver.registerContentObserver( Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), false, mSystemAutoRotateObserver); updateAutoRotateSetting(); } } public void destroy() { if (!mDestroyed) { mDestroyed = true; if (mSharedPrefs != null) { mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); } mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver); } } private void notifyChange() { if (!mInitialized || mDestroyed) { return; } final int activityFlags; if (mStateHandlerRequest != REQUEST_NONE) { activityFlags = mStateHandlerRequest == REQUEST_LOCK ? SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; } else if (mCurrentTransitionRequest != REQUEST_NONE) { activityFlags = mCurrentTransitionRequest == REQUEST_LOCK ? SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; } else if (mCurrentStateRequest == REQUEST_LOCK) { activityFlags = SCREEN_ORIENTATION_LOCKED; } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE || mHomeRotationEnabled) { activityFlags = SCREEN_ORIENTATION_UNSPECIFIED; } else { // If auto rotation is off, allow rotation on the activity, in case the user is using // forced rotation. activityFlags = SCREEN_ORIENTATION_NOSENSOR; } if (activityFlags != mLastActivityFlags) { mLastActivityFlags = activityFlags; UiThreadHelper.setOrientationAsync(mActivity, activityFlags); } } /** * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... * A value of 0 means no rotation has been applied */ public static int deltaRotation(int oldRotation, int newRotation) { int delta = newRotation - oldRotation; if (delta < 0) delta += 4; return delta; } @Override public String toString() { return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d," + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b," + " mSystemAutoRotateEnabled=%b]", mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags, mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled); } }