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, String caller)108 public static void setRotationLock(Context context, final boolean enabled, String caller) { 109 final int rotation = areAllRotationsAllowed(context) 110 || useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION 111 : NATURAL_ROTATION; 112 setRotationLockAtAngle(context, enabled, rotation, caller); 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, String caller)118 public static void setRotationLockAtAngle(Context context, final boolean enabled, 119 final int rotation, String caller) { 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, caller); 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, String caller)132 public static void setRotationLockForAccessibility(Context context, final boolean enabled, 133 String caller) { 134 Settings.System.putIntForUser(context.getContentResolver(), 135 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0, 136 UserHandle.USER_CURRENT); 137 138 setRotationLock(enabled, NATURAL_ROTATION, caller); 139 } 140 areAllRotationsAllowed(Context context)141 private static boolean areAllRotationsAllowed(Context context) { 142 return context.getResources().getBoolean(R.bool.config_allowAllRotations); 143 } 144 useCurrentRotationOnRotationLockChange(Context context)145 private static boolean useCurrentRotationOnRotationLockChange(Context context) { 146 return context.getResources().getBoolean( 147 R.bool.config_useCurrentRotationOnRotationLockChange); 148 } 149 setRotationLock(final boolean enabled, final int rotation, final String caller)150 private static void setRotationLock(final boolean enabled, final int rotation, 151 final String caller) { 152 AsyncTask.execute(new Runnable() { 153 @Override 154 public void run() { 155 try { 156 IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); 157 if (enabled) { 158 wm.freezeRotation(rotation, caller); 159 } else { 160 wm.thawRotation(caller); 161 } 162 } catch (RemoteException exc) { 163 Log.w(TAG, "Unable to save auto-rotate setting"); 164 } 165 } 166 }); 167 } 168 169 /** 170 * Registers a listener for rotation policy changes affecting the caller's user 171 */ registerRotationPolicyListener(Context context, RotationPolicyListener listener)172 public static void registerRotationPolicyListener(Context context, 173 RotationPolicyListener listener) { 174 registerRotationPolicyListener(context, listener, UserHandle.getCallingUserId()); 175 } 176 177 /** 178 * Registers a listener for rotation policy changes affecting a specific user, 179 * or USER_ALL for all users. 180 */ registerRotationPolicyListener(Context context, RotationPolicyListener listener, int userHandle)181 public static void registerRotationPolicyListener(Context context, 182 RotationPolicyListener listener, int userHandle) { 183 context.getContentResolver().registerContentObserver(Settings.System.getUriFor( 184 Settings.System.ACCELEROMETER_ROTATION), 185 false, listener.mObserver, userHandle); 186 context.getContentResolver().registerContentObserver(Settings.System.getUriFor( 187 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY), 188 false, listener.mObserver, userHandle); 189 } 190 191 /** 192 * Unregisters a listener for rotation policy changes. 193 */ unregisterRotationPolicyListener(Context context, RotationPolicyListener listener)194 public static void unregisterRotationPolicyListener(Context context, 195 RotationPolicyListener listener) { 196 context.getContentResolver().unregisterContentObserver(listener.mObserver); 197 } 198 199 /** 200 * Listener that is invoked whenever a change occurs that might affect the rotation policy. 201 */ 202 public static abstract class RotationPolicyListener { 203 final ContentObserver mObserver = new ContentObserver(new Handler()) { 204 @Override 205 public void onChange(boolean selfChange, Uri uri) { 206 RotationPolicyListener.this.onChange(); 207 } 208 }; 209 onChange()210 public abstract void onChange(); 211 } 212 }