一般手机只能旋转3个方向,这里将介绍如何让手机可以旋转180度,也就是上下颠倒。
1.静态方法
frameworks/base/core/res/res/values/config.xml
config_allowAllRotations通过这个来设置。
<bool name="config_supportAutoRotation">truebool>
<bool name="config_allowAllRotations">truebool>
2.动态方法
在手机确认要旋转后,就会走PhoneWindowManager.java的onProposedRotationChanged。
class MyOrientationListener extends WindowOrientationListener {
private final Runnable mUpdateRotationRunnable = new Runnable() {
@Override
public void run() {
// send interaction hint to improve redraw performance
mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
updateRotation(false);
}
};
MyOrientationListener(Context context, Handler handler) {
super(context, handler);
}
@Override
public void onProposedRotationChanged(int rotation) {
if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
mHandler.post(mUpdateRotationRunnable);
}
}
然后走
void updateRotation(boolean alwaysSendConfiguration) {
try {
//set orientation on WindowManager
mWindowManager.updateRotation(alwaysSendConfiguration, false);
} catch (RemoteException e) {
// Ignore
}
}
再走WindowManagerService.java的updateRotation。
/**
* Recalculate the current rotation.
*
* Called by the window manager policy whenever the state of the system changes
* such that the current rotation might need to be updated, such as when the
* device is docked or rotated into a new posture.
*/
@Override
public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}
再走
private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"
+ " alwaysSendConfiguration=" + alwaysSendConfiguration
+ " forceRelayout=" + forceRelayout);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
long origId = Binder.clearCallingIdentity();
try {
// TODO(multi-display): Update rotation for different displays separately.
final boolean rotationChanged;
final int displayId;
synchronized (mWindowMap) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
rotationChanged = displayContent.updateRotationUnchecked(
false /* inTransaction */);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (!rotationChanged || forceRelayout) {
displayContent.setLayoutNeeded();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"updateRotation: performSurfacePlacement");
mWindowPlacerLocked.performSurfacePlacement();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
displayId = displayContent.getDisplayId();
}
if (rotationChanged || alwaysSendConfiguration) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");
sendNewConfiguration(displayId);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
} finally {
Binder.restoreCallingIdentity(origId);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
再走DisplayContent.java的updateRotationUnchecked
/**
* Update rotation of the display.
*
* Returns true if the rotation has been changed. In this case YOU MUST CALL
* {@link WindowManagerService#sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
*/
boolean updateRotationUnchecked(boolean inTransaction) {
if (mService.mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until
// updates have been resumed.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
return false;
}
ScreenRotationAnimation screenRotationAnimation =
mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
// Rotation updates cannot be performed while the previous rotation change
// animation is still in progress. Skip this update. We will try updating
// again after the animation is finished and the display is unfrozen.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
return false;
}
if (mService.mDisplayFrozen) {
// Even if the screen rotation animation has finished (e.g. isAnimating
// returns false), there is still some time where we haven't yet unfrozen
// the display. We also need to abort rotation here.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
"Deferring rotation, still finishing previous rotation");
return false;
}
if (!mService.mDisplayEnabled) {
// No point choosing a rotation if the display is not enabled.
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
return false;
}
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
final boolean oldAltOrientation = mAltOrientation;
int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
final boolean rotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
rotation);
if (rotateSeamlessly) {
final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
if (seamlessRotated != null) {
// We can't rotate (seamlessly or not) while waiting for the last seamless rotation
// to complete (that is, waiting for windows to redraw). It's tempting to check
// w.mSeamlessRotationCount but that could be incorrect in the case of
// window-removal.
return false;
}
}
。。。。。。
}
最后走PhoneWindowManager.java的rotationForOrientationLw
@Override
public int rotationForOrientationLw(int orientation, int lastRotation) {
if (false) {
Slog.v(TAG, "rotationForOrientationLw(orient="
+ orientation + ", last=" + lastRotation
+ "); user=" + mUserRotation + " "
+ ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED)
? "USER_ROTATION_LOCKED" : "")
);
//Aaron add
mAllowAllRotations = -1;
if (mForceDefaultOrientation) {
return Surface.ROTATION_0;
}
synchronized (mLock) {
int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1
if (sensorRotation < 0) {
sensorRotation = lastRotation;
}
final int preferredRotation;
if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
// Ignore sensor when lid switch is open and rotation is forced.
preferredRotation = mLidOpenRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
&& (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
// Ignore sensor when in car dock unless explicitly enabled.
// This case can override the behavior of NOSENSOR, and can also
// enable 180 degree rotation while docked.
preferredRotation = mCarDockEnablesAccelerometer
? sensorRotation : mCarDockRotation;
} else if ((mDockMode == Intent.EXTRA_DOCK_STATE_DESK
|| mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK
|| mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)
&& (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
// Ignore sensor when in desk dock unless explicitly enabled.
// This case can override the behavior of NOSENSOR, and can also
// enable 180 degree rotation while docked.
preferredRotation = mDeskDockEnablesAccelerometer
? sensorRotation : mDeskDockRotation;
} else if ((mHdmiPlugged || mWifiDisplayConnected) && mDemoHdmiRotationLock) {
// Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
// Note that the dock orientation overrides the HDMI orientation.
preferredRotation = mDemoHdmiRotation;
} else if (mWifiDisplayConnected && (mWifiDisplayCustomRotation > -1)) {
// Ignore sensor when WFD is active and UIBC rotation is enabled
preferredRotation = mWifiDisplayCustomRotation;
} else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
&& mUndockedHdmiRotation >= 0) {
// Ignore sensor when plugged into HDMI and an undocked orientation has
// been specified in the configuration (only for legacy devices without
// full multi-display support).
// Note that the dock orientation overrides the HDMI orientation.
preferredRotation = mUndockedHdmiRotation;
} else if (mDemoRotationLock) {
// Ignore sensor when demo rotation lock is enabled.
// Note that the dock orientation and HDMI rotation lock override this.
preferredRotation = mDemoRotation;
} else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
// Application just wants to remain locked in the last rotation.
preferredRotation = lastRotation;
} else if (!mSupportAutoRotation) {
// If we don't support auto-rotation then bail out here and ignore
// the sensor and any rotation lock settings.
preferredRotation = -1;
} else if ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
&& (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
|| orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|| orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|| orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
// Otherwise, use sensor only if requested by the application or enabled
// by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR.
if (mAllowAllRotations < 0) {
// Can't read this during init() because the context doesn't
// have display metrics at that time so we cannot determine
// tablet vs. phone then.
//这里注释掉原来的 添加自己修改的
//Aaron change
/*mAllowAllRotations = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;*/
mAllowAllRotations = Global.getInt(mContext.getContentResolver(), Global.ALLOW_ALL_ROTATION_ON,0);
int enable = Global.getInt(mContext.getContentResolver(), Global.AUTO_ROTATION, 0);
if (enable != 1)
mAllowAllRotations = -1;
}
if (sensorRotation != Surface.ROTATION_180
|| mAllowAllRotations == 1
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
// In VrMode, we report the sensor as always being in default orientation so:
// 1) The orientation doesn't change as the user moves their head.
// 2) 2D apps within VR show in the device's default orientation.
// This only overwrites the sensor-provided orientation and does not affect any
// explicit orientation preferences specified by any activities.
preferredRotation =
mPersistentVrModeEnabled ? Surface.ROTATION_0 : sensorRotation;
} else {
preferredRotation = lastRotation;
}
} 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.
// We will do exactly what the application asked us to do.
preferredRotation = -1;
}
switch (orientation) {
case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
// Return portrait unless overridden.
if (isAnyPortrait(preferredRotation)) {
return preferredRotation;
}
return mPortraitRotation;
case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
// Return landscape unless overridden.
if (isLandscapeOrSeascape(preferredRotation)) {
return preferredRotation;
}
return mLandscapeRotation;
case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
// Return reverse portrait unless overridden.
if (isAnyPortrait(preferredRotation)) {
return preferredRotation;
}
return mUpsideDownRotation;
case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
// Return seascape unless overridden.
if (isLandscapeOrSeascape(preferredRotation)) {
return preferredRotation;
}
return mSeascapeRotation;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
// Return either landscape rotation.
if (isLandscapeOrSeascape(preferredRotation)) {
return preferredRotation;
}
if (isLandscapeOrSeascape(lastRotation)) {
return lastRotation;
}
return mLandscapeRotation;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
// Return either portrait rotation.
if (isAnyPortrait(preferredRotation)) {
return preferredRotation;
}
if (isAnyPortrait(lastRotation)) {
return lastRotation;
}
return mPortraitRotation;
default:
// For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
// just return the preferred orientation we already calculated.
if (preferredRotation >= 0) {
return preferredRotation;
}
return Surface.ROTATION_0;
}
}
}
这里主要就是修改mAllowAllRotations的值,虽然每次旋转都要走这个判断要不要选择180度,但是实际上就判断一次,因为mAllowAllRotations这个值变了之后就不会再走赋值的地方了,可以看代码。所以我们要每次都要去根据要不要选择180度来改变这个值,所以每次进来先mAllowAllRotations=-1,然后就会走到赋值的那个里面,进行赋值。
是否要旋转180度的数据库。
mAllowAllRotations = Global.getInt(mContext.getContentResolver(), Global.ALLOW_ALL_ROTATION_ON,0);
注意:如果先把180度关闭,打开自动选择,把手机选择180度,然后再打开180度开关,这时候屏幕不会选择180度,正确的,但是当你再关闭自动旋转的时候,屏幕会先选择180度,然后再旋转回来。有问题,所以多加了个判断。
当自动旋转打开的时候才能旋转180。数据库自己加的。
int enable = Global.getInt(mContext.getContentResolver(), Global.AUTO_ROTATION, 0);
if (enable != 1)
mAllowAllRotations = -1;