以通知栏中下拉菜单里面的"自动旋转"按钮为例子,说下横竖屏切换的原理;
点击状态栏中的"自动旋转"按钮,会走下面代码:
frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
protected void handleClick() {
//"自动旋转"的点击事件
if (mController == null) return;
final boolean newState = !mState.value;
//设置横竖屏,newState表示是否保存当前旋转方向
mController.setRotationLocked(!newState);
//刷新界面
refreshState(newState);
}
我们主要看的是,横竖屏是如何设置的,mController的实现类是RotationLockControllerImpl:
public void setRotationLocked(boolean locked) {
RotationPolicy.setRotationLock(mContext, locked);
}
进入到了RotationPolicy里面,继续看:
public static void setRotationLock(Context context, final boolean enabled) {
Settings.System.putIntForUser(context.getContentResolver(),
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
UserHandle.USER_CURRENT);
//屏幕旋转度数
final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
//设置旋转
setRotationLock(enabled, rotation);
}
private static void setRotationLock(final boolean enabled, final int rotation) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
if (enabled) {
//旋转屏幕,并保持旋转后的方向
wm.freezeRotation(rotation);
} else {
//可以自由旋转方向,也就是横竖屏自由切换
wm.thawRotation();
}
} catch (RemoteException exc) {
Log.w(TAG, "Unable to save auto-rotate setting");
}
}
});
}
可以看到,wm就是WMS,wm.freezeRotation(rotation)方法可以固定屏幕的旋转方向,rotation就是屏幕的旋转度数;
rotation只有两个值,CURRENT_ROTATION和NATURAL_ROTATION,分别是-1和0;
0我们知道是竖屏的意思,那这个-1呢,它表示的是固定当前屏幕的方向。假如我们在横屏时想固定横屏方向,就可以使用RotationPolicy.setRotationLock(context,true)函数;
private static final int CURRENT_ROTATION = -1;
private static final int NATURAL_ROTATION = Surface.ROTATION_0;
当然你想让屏幕旋转到其他位置也是可以的,Surface中设置了4种旋转角度:
/**
* Rotation constant: 0 degree rotation (natural orientation)
*/
public static final int ROTATION_0 = 0;
/**
* Rotation constant: 90 degree rotation.
*/
public static final int ROTATION_90 = 1;
/**
* Rotation constant: 180 degree rotation.
*/
public static final int ROTATION_180 = 2;
/**
* Rotation constant: 270 degree rotation.
*/
public static final int ROTATION_270 = 3;
但是,RotationPolicy并没有提供设置旋转角度的api,如果有这方面的需求,可以调用WMS.freezeRotation()函数;
接下来就看屏幕旋转是如何实现的;
public void freezeRotation(int rotation) {
// TODO(multi-display): Track which display is rotated.
if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
"freezeRotation()")) {
throw new SecurityException("Requires SET_ORIENTATION permission");
}
if (rotation < -1 || rotation > Surface.ROTATION_270) {
throw new IllegalArgumentException("Rotation argument must be -1 or a valid "
+ "rotation constant.");
}
//得到当前的屏幕旋转方向
final int defaultDisplayRotation = getDefaultDisplayRotation();
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation="
+ defaultDisplayRotation);
long origId = Binder.clearCallingIdentity();
try {
//保存新的屏幕方向,
//rotation == -1表示锁定当前的屏幕旋转方向,比如屏幕自由旋转中,旋转到了横屏,那就可以锁定横屏,一直保持横屏状态
mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,
rotation == -1 ? defaultDisplayRotation : rotation);
} finally {
Binder.restoreCallingIdentity(origId);
}
//更新屏幕状态,这个才是核心
updateRotationUnchecked(false, false);
}
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);
}
}
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;
}
//旋转动画,mService.mAnimator持有DimSurface,这个就是最终要使用的Surface
ScreenRotationAnimation screenRotationAnimation =
mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
......
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
final boolean oldAltOrientation = mAltOrientation;
//后续的计算都基于这个值
int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
rotation);
......
mRotation = rotation;
mAltOrientation = altOrientation;
if (isDefaultDisplay) {
//更新旋转状态,同步到WindowOrientationListener中
mService.mPolicy.setRotationLw(rotation);
}
......
return true;
}
其中mPolicy是PhoneWindowManager;
关于这个代码暂时就先不分析了,有机会再来继续看,今天的主要目的还是介绍下如何设置屏幕旋转状态;
完结,散花!