1 /* 2 * Copyright (C) 2012 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 17 package com.android.internal.view; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.content.res.Configuration; 22 import android.database.ContentObserver; 23 import android.net.Uri; 24 import android.os.AsyncTask; 25 import android.os.Handler; 26 import android.os.RemoteException; 27 import android.os.UserHandle; 28 import android.provider.Settings; 29 import android.util.DisplayMetrics; 30 import android.util.Log; 31 import android.view.IWindowManager; 32 import android.view.Surface; 33 import android.view.WindowManagerGlobal; 34 35 import com.android.internal.R; 36 37 /** 38 * Provides helper functions for configuring the display rotation policy. 39 */ 40 public final class RotationPolicy { 41 private static final String TAG = "RotationPolicy"; 42 private static final int CURRENT_ROTATION = -1; 43 44 public static final int NATURAL_ROTATION = Surface.ROTATION_0; 45 RotationPolicy()46 private RotationPolicy() { 47 } 48 49 /** 50 * Gets whether the device supports rotation. In general such a 51 * device has an accelerometer and has the portrait and landscape 52 * features. 53 * 54 * @param context Context for accessing system resources. 55 * @return Whether the device supports rotation. 56 */ isRotationSupported(Context context)57 public static boolean isRotationSupported(Context context) { 58 PackageManager pm = context.getPackageManager(); 59 return pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_ACCELEROMETER) 60 && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT) 61 && pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE) 62 && context.getResources().getBoolean( 63 com.android.internal.R.bool.config_supportAutoRotation); 64 } 65 66 /** 67 * Returns the orientation that will be used when locking the orientation from system UI 68 * with {@link #setRotationLock}. 69 * 70 * If the device only supports locking to its natural orientation, this will be either 71 * Configuration.ORIENTATION_PORTRAIT or Configuration.ORIENTATION_LANDSCAPE, 72 * otherwise Configuration.ORIENTATION_UNDEFINED if any orientation is lockable. 73 */ getRotationLockOrientation(Context context)74 public static int getRotationLockOrientation(Context context) { 75 if (areAllRotationsAllowed(context)) { 76 return Configuration.ORIENTATION_UNDEFINED; 77 } 78 final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 79 final int rotation = 80 context.getResources().getConfiguration().windowConfiguration.getRotation(); 81 final boolean rotated = rotation % 2 != 0; 82 final int w = rotated ? metrics.heightPixels : metrics.widthPixels; 83 final int h = rotated ? metrics.widthPixels : metrics.heightPixels; 84 return w < h ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; 85 } 86 87 /** 88 * Returns true if the rotation-lock toggle should be shown in system UI. 89 */ isRotationLockToggleVisible(Context context)90 public static boolean isRotationLockToggleVisible(Context context) { 91 return isRotationSupported(context) && 92 Settings.System.getIntForUser(context.getContentResolver(), 93 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0, 94 UserHandle.USER_CURRENT) == 0; 95 } 96 97 /** 98 * Returns true if rotation lock is enabled. 99 */ isRotationLocked(Context context)100 public static boolean isRotationLocked(Context context) { 101 return Settings.System.getIntForUser(context.getContentResolver(), 102 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0; 103 } 104 105 /** 106 * Enables or disables rotation lock from the system UI toggle. 107 */ setRotationLock(Context context, final boolean enabled)108 public static void setRotationLock(Context context, final boolean enabled) { 109 final int rotation = areAllRotationsAllowed(context) 110 || useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION 111 : NATURAL_ROTATION; 112 setRotationLockAtAngle(context, enabled, rotation); 113 } 114 115 /** 116 * Enables or disables rotation lock at a specific rotation from system UI. 117 */ setRotationLockAtAngle(Context context, final boolean enabled, final int rotation)118 public static void setRotationLockAtAngle(Context context, final boolean enabled, 119 final int rotation) { 120 Settings.System.putIntForUser(context.getContentResolver(), 121 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0, 122 UserHandle.USER_CURRENT); 123 124 setRotationLock(enabled, rotation); 125 } 126 127 /** 128 * Enables or disables natural rotation lock from Accessibility settings. 129 * 130 * If rotation is locked for accessibility, the system UI toggle is hidden to avoid confusion. 131 */ setRotationLockForAccessibility(Context context, final boolean enabled)132 public static void setRotationLockForAccessibility(Context context, final boolean enabled) { 133 Settings.System.putIntForUser(context.getContentResolver(), 134 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0, 135 UserHandle.USER_CURRENT); 136 137 setRotationLock(enabled, NATURAL_ROTATION); 138 } 139 areAllRotationsAllowed(Context context)140 private static boolean areAllRotationsAllowed(Context context) { 141 return context.getResources().getBoolean(R.bool.config_allowAllRotations); 142 } 143 useCurrentRotationOnRotationLockChange(Context context)144 private static boolean useCurrentRotationOnRotationLockChange(Context context) { 145 return context.getResources().getBoolean( 146 R.bool.config_useCurrentRotationOnRotationLockChange); 147 } 148 setRotationLock(final boolean enabled, final int rotation)149 private static void setRotationLock(final boolean enabled, final int rotation) { 150 AsyncTask.execute(new Runnable() { 151 @Override 152 public void run() { 153 try { 154 IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 155 if (enabled) { 156 wm.freezeRotation(rotation); 157 } else { 158 wm.thawRotation(); 159 } 160 } catch (RemoteException exc) { 161 Log.w(TAG, "Unable to save auto-rotate setting"); 162 } 163 } 164 }); 165 } 166 167 /** 168 * Registers a listener for rotation policy changes affecting the caller's user 169 */ registerRotationPolicyListener(Context context, RotationPolicyListener listener)170 public static void registerRotationPolicyListener(Context context, 171 RotationPolicyListener listener) { 172 registerRotationPolicyListener(context, listener, UserHandle.getCallingUserId()); 173 } 174 175 /** 176 * Registers a listener for rotation policy changes affecting a specific user, 177 * or USER_ALL for all users. 178 */ registerRotationPolicyListener(Context context, RotationPolicyListener listener, int userHandle)179 public static void registerRotationPolicyListener(Context context, 180 RotationPolicyListener listener, int userHandle) { 181 context.getContentResolver().registerContentObserver(Settings.System.getUriFor( 182 Settings.System.ACCELEROMETER_ROTATION), 183 false, listener.mObserver, userHandle); 184 context.getContentResolver().registerContentObserver(Settings.System.getUriFor( 185 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY), 186 false, listener.mObserver, userHandle); 187 } 188 189 /** 190 * Unregisters a listener for rotation policy changes. 191 */ unregisterRotationPolicyListener(Context context, RotationPolicyListener listener)192 public static void unregisterRotationPolicyListener(Context context, 193 RotationPolicyListener listener) { 194 context.getContentResolver().unregisterContentObserver(listener.mObserver); 195 } 196 197 /** 198 * Listener that is invoked whenever a change occurs that might affect the rotation policy. 199 */ 200 public static abstract class RotationPolicyListener { 201 final ContentObserver mObserver = new ContentObserver(new Handler()) { 202 @Override 203 public void onChange(boolean selfChange, Uri uri) { 204 RotationPolicyListener.this.onChange(); 205 } 206 }; 207 onChange()208 public abstract void onChange(); 209 } 210 }