diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8630204a79833..a90dd8c9eb05f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1749,6 +1749,20 @@ public final class Settings { */ public static final String USER_ROTATION = "user_rotation"; + /** + * Control whether the rotation lock toggle in the System UI should be hidden. + * Typically this is done for accessibility purposes to make it harder for + * the user to accidentally toggle the rotation lock while the display rotation + * has been locked for accessibility. + * + * If 0, then rotation lock toggle is not hidden for accessibility (although it may be + * unavailable for other reasons). If 1, then the rotation lock toggle is hidden. + * + * @hide + */ + public static final String HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY = + "hide_rotation_lock_toggle_for_accessibility"; + /** * Whether the phone vibrates when it is ringing due to an incoming call. This will * be used by Phone and Setting apps; it shouldn't affect other apps. @@ -2029,6 +2043,7 @@ public final class Settings { DATE_FORMAT, ACCELEROMETER_ROTATION, USER_ROTATION, + HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, DTMF_TONE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING, EMERGENCY_TONE, diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java new file mode 100644 index 0000000000000..af512a30cce92 --- /dev/null +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2012 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.internal.view; + +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.util.Log; +import android.view.IWindowManager; +import android.view.Surface; + +/** + * Provides helper functions for configuring the display rotation policy. + */ +public final class RotationPolicy { + private static final String TAG = "RotationPolicy"; + + private RotationPolicy() { + } + + /** + * Returns true if the device supports the rotation-lock toggle feature + * in the system UI or system bar. + * + * When the rotation-lock toggle is supported, the "auto-rotate screen" option in + * Display settings should be hidden, but it should remain available in Accessibility + * settings. + */ + public static boolean isRotationLockToggleSupported(Context context) { + return context.getResources().getConfiguration().smallestScreenWidthDp >= 600; + } + + /** + * Returns true if the rotation-lock toggle should be shown in the UI. + */ + public static boolean isRotationLockToggleVisible(Context context) { + return isRotationLockToggleSupported(context) && + Settings.System.getInt(context.getContentResolver(), + Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0) == 0; + } + + /** + * Returns true if rotation lock is enabled. + */ + public static boolean isRotationLocked(Context context) { + return Settings.System.getInt(context.getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION, 0) == 0; + } + + /** + * Enables or disables rotation lock. + * + * Should be used by the rotation lock toggle. + */ + public static void setRotationLock(Context context, final boolean enabled) { + Settings.System.putInt(context.getContentResolver(), + Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0); + + AsyncTask.execute(new Runnable() { + @Override + public void run() { + try { + IWindowManager wm = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + if (enabled) { + wm.freezeRotation(-1); + } else { + wm.thawRotation(); + } + } catch (RemoteException exc) { + Log.w(TAG, "Unable to save auto-rotate setting"); + } + } + }); + } + + /** + * Enables or disables rotation lock and adjusts whether the rotation lock toggle + * should be hidden for accessibility purposes. + * + * Should be used by Display settings and Accessibility settings. + */ + public static void setRotationLockForAccessibility(Context context, final boolean enabled) { + Settings.System.putInt(context.getContentResolver(), + Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0); + + AsyncTask.execute(new Runnable() { + @Override + public void run() { + try { + IWindowManager wm = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + if (enabled) { + wm.freezeRotation(Surface.ROTATION_0); + } else { + wm.thawRotation(); + } + } catch (RemoteException exc) { + Log.w(TAG, "Unable to save auto-rotate setting"); + } + } + }); + } + + /** + * Registers a listener for rotation policy changes. + */ + public static void registerRotationPolicyListener(Context context, + RotationPolicyListener listener) { + context.getContentResolver().registerContentObserver(Settings.System.getUriFor( + Settings.System.ACCELEROMETER_ROTATION), + false, listener.mObserver); + context.getContentResolver().registerContentObserver(Settings.System.getUriFor( + Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY), + false, listener.mObserver); + } + + /** + * Unregisters a listener for rotation policy changes. + */ + public static void unregisterRotationPolicyListener(Context context, + RotationPolicyListener listener) { + context.getContentResolver().unregisterContentObserver(listener.mObserver); + } + + /** + * Listener that is invoked whenever a change occurs that might affect the rotation policy. + */ + public static abstract class RotationPolicyListener { + final ContentObserver mObserver = new ContentObserver(new Handler()) { + public void onChange(boolean selfChange, Uri uri) { + RotationPolicyListener.this.onChange(); + } + }; + + public abstract void onChange(); + } +} \ No newline at end of file diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_expanded_header.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_expanded_header.xml deleted file mode 100644 index 9b834d26c7962..0000000000000 --- a/packages/SystemUI/res/layout-sw600dp/status_bar_expanded_header.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index 893d422398185..cb4e6a988b6a4 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -43,6 +43,15 @@ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" /> + + - + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java b/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java index c5a7354777434..5dd45a40e3b21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RotationToggle.java @@ -6,25 +6,39 @@ import android.widget.CompoundButton; import com.android.systemui.statusbar.policy.AutoRotateController; -public class RotationToggle extends CompoundButton { - AutoRotateController mRotater; +public class RotationToggle extends CompoundButton + implements AutoRotateController.RotationLockCallbacks { + private AutoRotateController mRotater; public RotationToggle(Context context) { super(context); - mRotater = new AutoRotateController(context, this); - setClickable(true); } public RotationToggle(Context context, AttributeSet attrs) { super(context, attrs); - mRotater = new AutoRotateController(context, this); - setClickable(true); } public RotationToggle(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mRotater = new AutoRotateController(context, this); - setClickable(true); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mRotater = new AutoRotateController(getContext(), this, this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mRotater != null) { + mRotater.release(); + mRotater = null; + } + } + + @Override + public void setRotationLockControlVisibility(boolean show) { + setVisibility(show ? VISIBLE : GONE); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java index 3d637813fce1f..109395cf3bb04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java @@ -16,80 +16,60 @@ package com.android.systemui.statusbar.policy; -import android.content.ContentResolver; +import com.android.internal.view.RotationPolicy; + import android.content.Context; -import android.database.ContentObserver; -import android.os.AsyncTask; -import android.os.Handler; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.provider.Settings; -import android.util.Log; -import android.view.IWindowManager; import android.widget.CompoundButton; -public class AutoRotateController implements CompoundButton.OnCheckedChangeListener { - private static final String TAG = "StatusBar.AutoRotateController"; - +public final class AutoRotateController implements CompoundButton.OnCheckedChangeListener { private final Context mContext; private final CompoundButton mCheckbox; + private final RotationLockCallbacks mCallbacks; private boolean mAutoRotation; - private ContentObserver mAccelerometerRotationObserver = new ContentObserver(new Handler()) { + private final RotationPolicy.RotationPolicyListener mRotationPolicyListener = + new RotationPolicy.RotationPolicyListener() { @Override - public void onChange(boolean selfChange) { - updateCheckbox(); + public void onChange() { + updateState(); } }; - public AutoRotateController(Context context, CompoundButton checkbox) { + public AutoRotateController(Context context, CompoundButton checkbox, + RotationLockCallbacks callbacks) { mContext = context; mCheckbox = checkbox; - updateCheckbox(); + mCallbacks = callbacks; + mCheckbox.setOnCheckedChangeListener(this); - mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), true, - mAccelerometerRotationObserver); + RotationPolicy.registerRotationPolicyListener(context, mRotationPolicyListener); + updateState(); } public void onCheckedChanged(CompoundButton view, boolean checked) { if (checked != mAutoRotation) { - setAutoRotation(checked); + mAutoRotation = checked; + RotationPolicy.setRotationLock(mContext, !checked); } } public void release() { - mContext.getContentResolver().unregisterContentObserver(mAccelerometerRotationObserver); + RotationPolicy.unregisterRotationPolicyListener(mContext, + mRotationPolicyListener); } - private void updateCheckbox() { - mAutoRotation = getAutoRotation(); + private void updateState() { + mAutoRotation = !RotationPolicy.isRotationLocked(mContext); mCheckbox.setChecked(mAutoRotation); + + boolean visible = RotationPolicy.isRotationLockToggleVisible(mContext); + mCallbacks.setRotationLockControlVisibility(visible); + mCheckbox.setEnabled(visible); } - private boolean getAutoRotation() { - ContentResolver cr = mContext.getContentResolver(); - return 0 != Settings.System.getInt(cr, Settings.System.ACCELEROMETER_ROTATION, 0); - } - - private void setAutoRotation(final boolean autorotate) { - mAutoRotation = autorotate; - AsyncTask.execute(new Runnable() { - public void run() { - try { - IWindowManager wm = IWindowManager.Stub.asInterface( - ServiceManager.getService(Context.WINDOW_SERVICE)); - if (autorotate) { - wm.thawRotation(); - } else { - wm.freezeRotation(-1); - } - } catch (RemoteException exc) { - Log.w(TAG, "Unable to save auto-rotate setting"); - } - } - }); + public interface RotationLockCallbacks { + void setRotationLockControlVisibility(boolean show); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java index 46ea9400e6c6e..537ff6678f162 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java @@ -43,6 +43,8 @@ public class SettingsView extends LinearLayout implements View.OnClickListener { AutoRotateController mRotate; BrightnessController mBrightness; DoNotDisturbController mDoNotDisturb; + View mRotationLockContainer; + View mRotationLockSeparator; public SettingsView(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -61,8 +63,19 @@ public class SettingsView extends LinearLayout implements View.OnClickListener { mAirplane = new AirplaneModeController(context, (CompoundButton)findViewById(R.id.airplane_checkbox)); findViewById(R.id.network).setOnClickListener(this); + + mRotationLockContainer = findViewById(R.id.rotate); + mRotationLockSeparator = findViewById(R.id.rotate_separator); mRotate = new AutoRotateController(context, - (CompoundButton)findViewById(R.id.rotate_checkbox)); + (CompoundButton)findViewById(R.id.rotate_checkbox), + new AutoRotateController.RotationLockCallbacks() { + @Override + public void setRotationLockControlVisibility(boolean show) { + mRotationLockContainer.setVisibility(show ? View.VISIBLE : View.GONE); + mRotationLockSeparator.setVisibility(show ? View.VISIBLE : View.GONE); + } + }); + mBrightness = new BrightnessController(context, (ToggleSlider)findViewById(R.id.brightness)); mDoNotDisturb = new DoNotDisturbController(context, diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 10332965da46b..9ef8d6bfaa6eb 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -346,6 +346,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; int mUserRotation = Surface.ROTATION_0; + boolean mAccelerometerDefault; int mAllowAllRotations = -1; boolean mCarDockEnablesAccelerometer; @@ -358,8 +359,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mScreenOnFully = false; boolean mOrientationSensorEnabled = false; int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - static final int DEFAULT_ACCELEROMETER_ROTATION = 0; - int mAccelerometerDefault = DEFAULT_ACCELEROMETER_ROTATION; boolean mHasSoftInput = false; int mPointerLocationMode = 0; // guarded by mLock @@ -617,7 +616,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // orientation management, return true; } - if (mAccelerometerDefault == 0) { + if (mUserRotationMode == USER_ROTATION_LOCKED) { // If the setting for using the sensor by default is enabled, then // we will always leave it on. Note that the user could go to // a window that forces an orientation that does not use the @@ -1076,19 +1075,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { mIncallPowerBehavior = Settings.Secure.getInt(resolver, Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT); - int accelerometerDefault = Settings.System.getInt(resolver, - Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION); - - // set up rotation lock state - mUserRotationMode = (accelerometerDefault == 0) - ? WindowManagerPolicy.USER_ROTATION_LOCKED - : WindowManagerPolicy.USER_ROTATION_FREE; - mUserRotation = Settings.System.getInt(resolver, - Settings.System.USER_ROTATION, - Surface.ROTATION_0); - if (mAccelerometerDefault != accelerometerDefault) { - mAccelerometerDefault = accelerometerDefault; + // Configure rotation lock. + int userRotation = Settings.System.getInt(resolver, + Settings.System.USER_ROTATION, Surface.ROTATION_0); + if (mUserRotation != userRotation) { + mUserRotation = userRotation; + updateRotation = true; + } + int userRotationMode = Settings.System.getInt(resolver, + Settings.System.ACCELEROMETER_ROTATION, 0) != 0 ? + WindowManagerPolicy.USER_ROTATION_FREE : + WindowManagerPolicy.USER_ROTATION_LOCKED; + if (mUserRotationMode != userRotationMode) { + mUserRotationMode = userRotationMode; + updateRotation = true; updateOrientationListenerLp(); } @@ -3670,7 +3671,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Ignore sensor when plugged into HDMI. // Note that the dock orientation overrides the HDMI orientation. preferredRotation = mHdmiRotation; - } else if ((mAccelerometerDefault != 0 /* implies not rotation locked */ + } else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR @@ -3693,8 +3694,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else { preferredRotation = lastRotation; } - } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) { - // Apply rotation lock. + } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED + && orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + // Apply rotation lock. Does not apply to NOSENSOR. + // The idea is that the user rotation expresses a weak preference for the direction + // of gravity and as NOSENSOR is never affected by gravity, then neither should + // NOSENSOR be affected by rotation lock (although it will be affected by docks). preferredRotation = mUserRotation; } else { // No overriding preference. @@ -4325,8 +4330,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mUserRotationMode="); pw.print(mUserRotationMode); pw.print(" mUserRotation="); pw.print(mUserRotation); pw.print(" mAllowAllRotations="); pw.println(mAllowAllRotations); - pw.print(prefix); pw.print("mAccelerometerDefault="); pw.print(mAccelerometerDefault); - pw.print(" mCurrentAppOrientation="); pw.println(mCurrentAppOrientation); + pw.print(prefix); pw.print(" mCurrentAppOrientation="); pw.println(mCurrentAppOrientation); pw.print(prefix); pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer); pw.print(" mDeskDockEnablesAccelerometer=");