Android13屏幕旋转的基本逻辑

embedded/2024/10/19 7:35:00/
1.问题

1.settings put system user_rotation 1是什么意思

答案:设置用户期望的屏幕转向,0代表:Surface.ROTATION_0竖屏;1代表:Surface.ROTATION_90横屏

2.设置user_rotation和GSensor哪个优先级更高,比如user_rotation = 0期待竖屏,但是打开屏幕旋转且处于横屏时,应该是横屏还是竖屏

答案:此时GSensor优先级更高,横屏显示,具体原因看第三个问题

3.systemui中的“自动旋转”按钮影响的是哪个数据和系统的值

Settings.System.ACCELEROMETER_ROTATION和DisplayRotation.userRotationMode:

    /** When not otherwise specified by the activity's screenOrientation, rotation should be* determined by the system (that is, using sensors). */public final int USER_ROTATION_FREE = 0;                                                                                                               /** When not otherwise specified by the activity's screenOrientation, rotation is set by* the user. */public final int USER_ROTATION_LOCKED = 1;USER_ROTATION_FREE :如果应用不指定屏幕方向,sensor传感器决定
USER_ROTATION_LOCKED:如果应用不指定屏幕方向,user决定方向,即user_rotation数据库值。

打开自动旋转时候设置的是Settings.System.ACCELEROMETER_ROTATION值会设置成1,代表,否则设置成0,这个值会直接影响DisplayRotation.userRotationMode的值:

final int userRotationMode = Settings.System.getIntForUser(resolver,Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) !=0? WindowManagerPolicy.USER_ROTATION_FREE: WindowManagerPolicy.USER_ROTATION_LOCKED;也就说如果打开了自动旋转,userRotationMode = USER_ROTATION_FREE,代表通过sensor决定;否则设置
成USER_ROTATION_LOCKED,由user_rotation决定
2.GSensor触发屏幕转向的流程分析

现在我们知道,屏幕方向旋转是通过加速度传感器或者重力传感器来获取,但Sensor上报的原始数据还得通过一系列计算得到最终的旋转角度,这部分逻辑在WindowOrientationListener.AccelSensorJudge类中进行计算转换,当Sensor收到onSensorChanged()回调后,对原始数据进行加工,最终得到屏幕方向旋转角度,并当方向旋转值发生变化时,调用WindowOrientationListener#onProposedRotationChanged()方法发起更新:

        public void onProposedRotationChanged(int rotation) {ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);// Send interaction power boost to improve redraw performance.mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);if (isRotationChoicePossible(mCurrentAppOrientation)) {final boolean isValid = isValidRotationChoice(rotation);sendProposedRotationChangeToStatusBarInternal(rotation, isValid);} else {mService.updateRotation(false /* alwaysSendConfiguration */,false /* forceRelayout */);}}

mService.updateRotation->wms.updateRotationUnchecked->displayContent.updateRotationUnchecked->DisplayRotation.updateRotationUnchecked如下:

/*** Update rotation with an option to force the update. This updates the container's perception* of rotation and, depending on the top activities, will freeze the screen or start seamless* rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}* during {@link DisplayContent#sendNewConfiguration}.** @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating*                    orientation because we're waiting for some rotation to finish or display*                    to unfreeze, which results in configuration of the previously visible*                    activity being applied to a newly visible one. Forcing the rotation*                    update allows to workaround this issue.* @return {@code true} if the rotation has been changed. In this case YOU MUST CALL*         {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE*         THE SCREEN.*/boolean updateRotationUnchecked(boolean forceUpdate) {final int displayId = mDisplayContent.getDisplayId();if (!forceUpdate) {if (mDeferredRotationPauseCount > 0) {// Rotation updates have been paused temporarily. Defer the update until updates// have been resumed.ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");return false;}final ScreenRotationAnimation screenRotationAnimation =mDisplayContent.getRotationAnimation();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.ProtoLog.v(WM_DEBUG_ORIENTATION, "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.ProtoLog.v(WM_DEBUG_ORIENTATION,"Deferring rotation, still finishing previous rotation");return false;}if (mDisplayContent.mFixedRotationTransitionListener.shouldDeferRotation()) {// Makes sure that after the transition is finished, updateOrientation() can see// the difference from the latest orientation source.mLastOrientation = SCREEN_ORIENTATION_UNSET;// During the recents animation, the closing app might still be considered on top.// In order to ignore its requested orientation to avoid a sensor led rotation (e.g// user rotating the device while the recents animation is running), we ignore// rotation update while the animation is running.return false;}}if (!mService.mDisplayEnabled) {// No point choosing a rotation if the display is not enabled.ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled.");return false;}//当前(未更新)的屏幕转向final int oldRotation = mRotation;//最近一次应用请求的转向final int lastOrientation = mLastOrientation;//根据给定的显示方向确定一个屏幕方向final int rotation = rotationForOrientation(lastOrientation, oldRotation);ProtoLog.v(WM_DEBUG_ORIENTATION,"Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and "+ "oldRotation=%s (%d)",Surface.rotationToString(rotation), rotation,displayId,ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,Surface.rotationToString(oldRotation), oldRotation);ProtoLog.v(WM_DEBUG_ORIENTATION,"Display id=%d selected orientation %s (%d), got rotation %s (%d)", displayId,ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,Surface.rotationToString(rotation), rotation);if (oldRotation == rotation) {// No change.return false;}// Preemptively cancel the running recents animation -- SysUI can't currently handle this// case properly since the signals it receives all happen post-change. We do this earlier// in the rotation flow, since DisplayContent.updateDisplayOverrideConfigurationLocked seems// to happen too late.final RecentsAnimationController recentsAnimationController =mService.getRecentsAnimationController();if (recentsAnimationController != null) {recentsAnimationController.cancelAnimationForDisplayChange();}ProtoLog.v(WM_DEBUG_ORIENTATION,"Display id=%d rotation changed to %d from %d, lastOrientation=%d",displayId, rotation, oldRotation, lastOrientation);if (deltaRotation(oldRotation, rotation) != Surface.ROTATION_180) {mDisplayContent.mWaitingForConfig = true;}mRotation = rotation;mDisplayContent.setLayoutNeeded();if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();final TransitionRequestInfo.DisplayChange change = wasCollecting ? null: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),oldRotation, mRotation);mDisplayContent.requestChangeTransitionIfNeeded(ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);if (wasCollecting) {// Use remote-rotation infra since the transition has already been requested// TODO(shell-transitions): Remove this once lifecycle management can cover all//                          rotation cases.startRemoteRotation(oldRotation, mRotation);}return true;}mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {// The screen rotation animation uses a screenshot to freeze the screen while windows// resize underneath. When we are rotating seamlessly, we allow the elements to// transition to their rotated state independently and without a freeze required.prepareSeamlessRotation();} else {prepareNormalRotationAnimation();}// Give a remote handler (system ui) some time to reposition things.startRemoteRotation(oldRotation, mRotation);return true;}

其中特别重要的函数时rotationForOrientation,根据当下屏幕方向和应用请求的方向,决定最终的屏幕显示方向,看下其实现方式:

int rotationForOrientation(@ScreenOrientation int orientation,@Surface.Rotation int lastRotation) {ProtoLog.v(WM_DEBUG_ORIENTATION,"rotationForOrientation(orient=%s (%d), last=%s (%d)); user=%s (%d) %s",ActivityInfo.screenOrientationToString(orientation), orientation,Surface.rotationToString(lastRotation), lastRotation,Surface.rotationToString(mUserRotation), mUserRotation,mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED? "USER_ROTATION_LOCKED" : "");//用户锁定了方向,直接返回user_rotation数据设置的用户期待的值if (isFixedToUserRotation()) {return mUserRotation;}//获取Sensor检测到的屏幕方向值int sensorRotation = mOrientationListener != null? mOrientationListener.getProposedRotation() // may be -1: -1;mLastSensorRotation = sensorRotation;if (sensorRotation < 0) {sensorRotation = lastRotation;}final int lidState = mDisplayPolicy.getLidState();final int dockMode = mDisplayPolicy.getDockMode();final boolean hdmiPlugged = mDisplayPolicy.isHdmiPlugged();final boolean carDockEnablesAccelerometer =mDisplayPolicy.isCarDockEnablesAccelerometer();final boolean deskDockEnablesAccelerometer =mDisplayPolicy.isDeskDockEnablesAccelerometer();final int preferredRotation;if (!isDefaultDisplay) {// For secondary displays we ignore things like displays sensors, docking mode and// rotation lock, and always prefer user rotation.preferredRotation = mUserRotation;} else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {// Ignore sensor when lid switch is open and rotation is forced.preferredRotation = mLidOpenRotation;} else if (dockMode == Intent.EXTRA_DOCK_STATE_CAR&& (carDockEnablesAccelerometer || 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 = carDockEnablesAccelerometer ? sensorRotation : mCarDockRotation;} else if ((dockMode == Intent.EXTRA_DOCK_STATE_DESK|| dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK|| dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK)&& (deskDockEnablesAccelerometer || 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 = deskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation;} else if (hdmiPlugged && 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 (hdmiPlugged && dockMode == 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 (mDisplayPolicy.isPersistentVrModeEnabled()) {// While in VR, apps always prefer a portrait rotation. This does not change// any apps that explicitly set landscape, but does cause sensors be ignored,// and ignored any orientation lock that the user has set (this conditional// should remain above the ORIENTATION_LOCKED conditional below).preferredRotation = mPortraitRotation;} 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 (sensorRotation != Surface.ROTATION_180|| getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {preferredRotation = sensorRotation;} else {preferredRotation = lastRotation;}} else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED&& orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR&& orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE&& orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT&& orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE&& orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {// Apply rotation lock. Does not apply to NOSENSOR or specific rotations.// 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).// Also avoid setting user rotation when app has preference over one particular rotation// to avoid leaving the rotation to the reverse of it which has the compatible// orientation, but isn't what app wants, when the user rotation is the reverse of the// preferred rotation.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;}}

参考文章:

安卓开发之屏幕旋转_安卓开发 屏幕旋转逻辑-CSDN博客

https://juejin.cn/post/6958440883276496927

https://juejin.cn/post/6982153383880687624


http://www.ppmy.cn/embedded/40924.html

相关文章

驱动丹佛斯比例电磁铁放大器

驱动丹佛斯比例电磁铁是一种用于实现对液压系统连续且精确控制的通电带磁性装置。比例阀由直流比例电磁铁和液压阀两部分组成。其中&#xff0c;比例电磁铁是其核心部件&#xff0c;负责将输入的电信号转换成力和位移输出&#xff0c;从而控制液压阀的工作状态。比例电磁铁通过…

IT行业现状与未来趋势-技术创新日新月异

目录 一、引言 二、IT行业现状 技术创新日新月异 市场需求持续增长 人才竞争激烈 网络安全问题凸显 三、IT行业未来趋势 人工智能将更加普及 区块链技术将改变商业模式 网络安全将成为重要战略 数字化转型将加速推进 四、结语 一、引言 随着科技的飞速发展&#x…

spacy微调BERT-NER模型

数据准备 加载数据集 from tqdm.notebook import tqdm import osdataset [] with open(train_file, r) as file:for line in tqdm(file.readlines()):data json.loads(line.strip())dataset.append(data)你可以按照 CLUENER 的格式准备训练数据&#xff0c; 例如&#xff1…

享元模式详解

享元模式 1 概述 定义&#xff1a; ​ 运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销&#xff0c;从而提高系统资源的利用率。 2 结构 享元&#xff08;Flyweight &#xff09;模式中存…

免费思维13招之十:增值型思维

免费思维13招之十:增值型思维 免费思维的另一大战略思维——增值型思维。 为了提高客户的粘性而促进重复性消费,我们必须对客户进行免费的增值型服务。 大家不要把增值型思维与赠品型思维混淆,增值型思维重心在于提高与消费者的粘性而促进重复消费,重心在后端。而赠品型思…

【Web后端】实现文件上传

表单必须使用post提交 ,enctype 必须是multipart/form-data在Servlet上填加注解 MultipartConfiglocation &#xff1a;默认情况下将存储文件的目录&#xff0c;默认值为“”。maxFileSize &#xff1a;允许上传文件的最大大小&#xff0c;其值以字节为单位。 默认值为-1L表示无…

YOLOv9改进策略目录 | 包含卷积、主干、检测头、注意力机制、Neck上百种创新机制

&#x1f451; YOLOv9有效涨点专栏目录 &#x1f451; 专栏视频介绍&#xff1a;包括专栏介绍、得到的项目文件、模型二次创新、权重文件的使用问题&#xff0c;点击即可跳转。 前言 Hello&#xff0c;各位读者们好 本专栏自开设两个月以来已经更新改进教程50余篇其中包含Re…

2024年4月Web3行业月度发展报告区块链篇 |陀螺研究院

4月&#xff0c;减半如期而至&#xff0c;但市场却略显平淡。在宏观降息预期放缓与ETF净流入收缩下&#xff0c;4月主流币种表现相对平缓&#xff0c;继地缘冲突导致比特币闪崩后&#xff0c;比特币持续在6.2-6.5万美元波动震荡&#xff0c;市场处于减半后的疲乏期&#xff0c;…