需求:某个app横屏显示不全,需要强制它竖屏显示,强制APP旋转优先级>系统方向优先级
如果系统没有强制横竖屏,一般都是默认应用本身的方向设置!
./frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
rotationForOrientation()和updateOrientation() 来负责修改当前app的显示方向
@Surface.Rotation
int rotationForOrientation(@ScreenOrientation int orientation,
@Surface.Rotation int lastRotation) {
...
} else {
// No overriding preference.
// We will do exactly what the application asked us to do.
preferredRotation = -1;
}
String rot = SystemProperties.get("persist.sys.app.rotation", "middle_port");
//add start 动态控制
if (rot.equals("force_landscape_customer")) {
orientation = mLandscapeRotation;//强制Activity显示横
} else if (rot.equals("force_portrait_customer")) {
orientation = mPortraitRotation;//强制Activity显示竖
}
//add end
if (rot.equals("force_land") && "box".equals(SystemProperties.get("ro.target.product"))) {
Slog.v(TAG, "asx force_land :" + mLandscapeRotation);
return mLandscapeRotation;
}
//根据orientation 来显示应用方向
switch (orientation) {
case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT://如果是竖屏
// Return portrait unless overridden.
if (isAnyPortrait(preferredRotation)) {
return preferredRotation;
}
/*如果不要动态根据参数修改,前面的拦截add start 部分注释掉,然后直接在switch里面改,把return mPortraitRotation;
改成 return mLandscapeRotation;或者return Surface.ROTATION_90 */
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;
...
}
boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
if (newOrientation == mLastOrientation && !forceUpdate) {
return false;
}
mLastOrientation = newOrientation;
if (newOrientation != mCurrentAppOrientation) {
mCurrentAppOrientation = newOrientation;
String rot = SystemProperties.get("persist.sys.app.rotation", "middle_port");//系统给了一个原生的,就用这个,如果系统没有给,那就自己创建
//add start
if (rot.equals("force_landscape_customer")) {
mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
} else if (rot.equals("force_portrait_customer")) {
mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
}
//add end
if (rot.equals("force_land") && "box".equals(SystemProperties.get("ro.target.product")))
mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
if (isDefaultDisplay) {
updateOrientationListenerLw();
}
}
return updateRotationUnchecked(forceUpdate);
}
2.1 切换横屏时SystemUI导航栏固定在桌面右侧而不是底部
R.bool.config_navBarCanMove 是否固定跟这个变量有关系,SystemUI导航栏跟随旋转false 不跟随旋转true
+++ b/frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2895,9 +2895,10 @@ public class DisplayPolicy {
void updateConfigurationAndScreenSizeDependentBehaviors() {
final Resources res = getCurrentUserResources();
- mNavigationBarCanMove =
- mDisplayContent.mBaseDisplayWidth != mDisplayContent.mBaseDisplayHeight
- && res.getBoolean(R.bool.config_navBarCanMove);
+ //mNavigationBarCanMove =
+ // mDisplayContent.mBaseDisplayWidth != mDisplayContent.mBaseDisplayHeight
+ // && res.getBoolean(R.bool.config_navBarCanMove);
+ mNavigationBarCanMove = false;
mDisplayContent.getDisplayRotation().updateUserDependentConfiguration(res);
}
An_Times
2.2 Android11 强制所有应用跟随 Gsensor旋转
处理app旋转的地方位于frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java中的rotationForOrientation()函数,
我们在这个函数进行拦截,rotationForOrientation函数第一个参数orientation,就是app的属性值了,我们把这个属性值强制改成所有方向跟随重力感应方向显示。
@Surface.Rotation
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" : "");
orientation = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;//add text //跟随G_Sensor旋转
if (isFixedToUserRotation()) {
return mUserRotation;
}
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
if (sensorRotation < 0) {
sensorRotation = lastRotation;//sensorRotation 是G_Sensor角度
}
...
2.3 强制所有app竖屏(Android R),横屏为0
--- a/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2403,7 +2403,11 @@ class DisplayContent extends WindowContainer @Override int getOrientation() { mLastOrientationSource = null; - + //add text start + if(true){ + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;//SCREEN_ORIENTATION_LANDSCAPE + } + //add text start if (mIgnoreRotationForApps) { return SCREEN_ORIENTATION_USER; } diff --git a/frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java b/frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java index 62cd6ad3a4..38ae33b2ef 100755 --- a/frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java @@ -120,7 +120,7 @@ public class DisplayRotation { * @see #updateRotationUnchecked */ @Surface.Rotation - private int mRotation; + private int mRotation = Integer.valueOf(SystemProperties.get("pw.sys.app.rotation","1")); @VisibleForTesting int mLandscapeRotation; // default landscape @@ -364,12 +364,12 @@ public class DisplayRotation { @Surface.Rotation int getRotation() { - return mRotation; + return Integer.valueOf(SystemProperties.get("pw.sys.app.rotation","1"));//mRotation; } @ScreenOrientation int getLastOrientation() { - return mLastOrientation; + return Integer.valueOf(SystemProperties.get("pw.sys.app.rotation","1"));//mLastOrientation; } 这个修改会影响二阶段的开机动画. 强制APP横竖屏方向_android 强制横屏- Android 11 系统默认横屏显示_高通android11默认横屏 2.4 某个三方应用强制全屏(横屏) 设备默认固定横屏,分辨率1280x800,某些三方应用显示为竖屏, ui 竖着显示在中间部分,设备左右两边为黑(xx)的 如: ________________________ | | | left mid right | | xx UI xx | | | | | | | |————————————————————————| bug演示 原因:三方应用默认竖屏,让它变为横屏显示 +++ b/release/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java public class ParsedActivityUtils { activity.configChanges = PackageParser.getActivityConfigChanges( sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0), sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0)); - //add text start - int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED); + + int screenOrientation; + if (pkg.getSharedUserId() == null){ + screenOrientation = 0; + }else{ + screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED); + } + //add text end int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation); activity.screenOrientation = screenOrientation; activity.resizeMode = resizeMode; +++ b/core/java/android/app/Activity.java public class Activity extends ContextThemeWrapper if (mParent == null) { try { //add text start system uid < 10000 建议用包名过滤 + if(mApplication != null && mApplication.getApplicationInfo() != null + && mApplication.getApplicationInfo().uid > 10000){ + ActivityTaskManager.getService().setRequestedOrientation( + mToken, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + }else{ + ActivityTaskManager.getService().setRequestedOrientation( + mToken, requestedOrientation); - ActivityManager.getService().setRequestedOrientation( - mToken, requestedOrientation); - mToken, 0); } } catch (RemoteException e) { // Empty } } else { + if(mApplication != null && mApplication.getApplicationInfo() != null + && mApplication.getApplicationInfo().uid > 10000){ + mParent.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + }else{ + mParent.setRequestedOrientation(requestedOrientation); + } - mParent.setRequestedOrientation(requestedOrientation); } } //add text end 2.5 竖屏app在大屏上的应用显示的屏占比问题 某些app没有适配大屏,默认竖屏,如果在大屏上运行app,显示的界面大小默为手机显示的界面大小,不方便操作. 可以利用上述强制横屏,达成全屏的效果,但是有些可能显示的UI细节模糊. 另外一种方法就是修改app界面的纵横比(宽高).如下: Android 11 和 Android 13 之间细节有些不同,大致是一样的. app的界面纵横比(宽高)控制:ActivityRecord::applyAspectRatio();[Android 11] 修改diff [Android 13] frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) { ... // Store the current bounds to be able to revert to size compat mode values below if needed. final Rect prevResolvedBounds = new Rect(resolvedBounds); resolvedBounds.set(containingBounds); final float letterboxAspectRatioOverride = mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(newParentConfig); //add text start /*final float desiredAspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);*/ //这个比例是自己需要 app ui size 算出来的 比如 1300 / 1800 final float desiredAspectRatio = 1.25f; //add text end // Apply aspect ratio to resolved bounds mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets, containingBounds, desiredAspectRatio); ... } private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds, float desiredAspectRatio) { ... if (adjustWidth) { - activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f); + //only update ORIENTATION_PORTRAIT'app layout + activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f); } else { activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f); } if (containingAppWidth <= activityWidth && containingAppHeight <= activityHeight) { // The display matches or is less than the activity aspect ratio, so nothing else to do. return false; } ... } 延申: 修改app显示区域 layoutWindowLw是DisplayPolicy.java中的一个关键函数,主要用于计算和布局窗口. 计算窗口显示区域:layoutWindowLw函数首先计算窗口的显示区域, 包括父容器显示区域(pf:ParentFrame)、设备的屏幕大小(df:DeviceFrame)、设备的屏幕大小(of:OverscanFrame)、 窗口内容显示区域(cf:ContentFrame)、可见区域(vf:VisibleFrame)、装饰区域大小,移除状态栏和导航栏(dcf:DecorFrame). 窗口布局和放置:计算完毕后,调用win.computeFrameLw(pf, df, of, cf, vf, dcf)进行具体的校验和赋值操作,最终确定窗口的大小和位置 ./frameworks/base/services/core/java/com/android/server/wm/WindowState.java:1049: public void computeFrameLw() demo:无聊的尝试 ./frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { ... if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { final @InsetsType int typesToFit = attrs.getFitInsetsTypes(); final @InsetsSide int sidesToFit = attrs.getFitInsetsSides(); final ArraySet final Rect dfu = displayFrames.mUnrestricted; Insets insets = Insets.of(0, 0, 0, 0); for (int i = types.size() - 1; i >= 0; i--) { final InsetsSource source = mDisplayContent.getInsetsPolicy() .getInsetsForDispatch(win).peekSource(types.valueAt(i)); if (source == null) { continue; } insets = Insets.max(insets, source.calculateInsets( dfu, attrs.isFitInsetsIgnoringVisibility())); } //add text start /*final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0; final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0; final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0; final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0;*/ final int left = 20; final int top = 10; final int right = 20; final int bottom = 10; //add text end df.set(left, top, dfu.right - right, dfu.bottom - bottom); if (attached == null) { pf.set(df); ... } //大屏设备上有部分app对话框显示不全,需要重新计算一下对话框的宽度,dialog显示是基于屏幕的宽度来的。 ./frameworks/base/core/java/android/app/Dialog.java public class Dialog implements DialogInterface, Window.Callback,...{ ... public void show() { if (mShowing) { if (mDecor != null) { if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); } mDecor.setVisibility(View.VISIBLE); } return; } mCanceled = false; ... + int disWidth = mWindowManager.getDefaultDisplay().getWidth(); + if(l.width > disWidth) { + l.width /= 2; + } mWindowManager.addView(mDecor, l); ... } Android系统,无源码apk分辨率适配 Android Framework 窗口子系统 (04) 确定窗口尺寸