强制app横屏显示或者竖屏显示(动态)

强制app横屏显示或者竖屏显示(动态)

需求:某个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 types = InsetsState.toInternalType(typesToFit);

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) 确定窗口尺寸

相关推荐

“异宠”知多少丨“放屁虫”-——蝽象
世界杯胜负预测技巧揭秘掌握数据与心理战提升分析水平
你衣服上的毛领是真的么,买的值不值?教你识别毛领的真假贵贱!