Android 8.0 Only fullscreen opaque activities can request orientation problem (Hook way to bypass the check elegantly)

Foreword:

Encountered on android 8.0 Only fullscreen opaque activities can request orientation, in an elegant way, Hook bypasses the check, no need to modify xml, or lower the target version.

Look at the configuration in the project that works normally in other versions of android :

Activity sets a black background and specifies that the screen is a vertical screen:
In xml, set the theme:

    <style name="Theme.picture" parent="AppTheme">

        <item name="colorPrimaryDark">#000000</item>
    </style>
    
    <activity
            android:name=".ui.Activity.myStory.xxxxActivity"
            android:screenOrientation="portrait"
            android:theme="@style/Theme.picture" />

Error reporting when running on Samsung android 8.0 system:

 Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
        at android.app.Activity.onCreate(Activity.java:1038)
        at android.support.v4.app.SupportActivity.onCreate(ComponentActivity.java:75)
        at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:335)
        at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:85)

Source Tracking

1. View the source code of oncreate() of api 27 Activity :

 protected void onCreate(@Nullable Bundle savedInstanceState) {
 
        if (getApplicationInfo().targetSdkVersion >= O_MR1 && mActivityInfo.isFixedOrientation()) {
            final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
            final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
            ta.recycle();

            if (isTranslucentOrFloating) {
                throw new IllegalStateException(
                        "Only fullscreen opaque activities can request orientation");
            }
        }
     
     
 }

Found more check screen operations. When condition 1 is met: fix the screen orientation. And meet the second condition: transparent color or suspension, an exception will be thrown.

2. View the screen orientation of the Activity

Next look: ActivityInfo.isFixedOrientation()check the screen orientation, whether it needs to be fixed

  /**
     * Returns true if the activity's orientation is fixed.
     * @hide
     */
    public boolean isFixedOrientation() {
        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
    }

3. Check whether the Activity is transparent or floating

Finally, let's take a look at it ActivityInfo.isTranslucentOrFloating()for checking whether the transparent color

 /**
     * Determines whether the {@link Activity} is considered translucent or floating.
     * @hide
     */
    public static boolean isTranslucentOrFloating(TypedArray attributes) {
        final boolean isTranslucent =
                attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent,
                        false);
        final boolean isSwipeToDismiss = !attributes.hasValue(
                com.android.internal.R.styleable.Window_windowIsTranslucent)
                && attributes.getBoolean(
                        com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
        final boolean isFloating =
                attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating,
                        false);

        return isFloating || isTranslucent || isSwipeToDismiss;
    }

Elegant way: Hook reflection bypass check

Ideas:

  • Determine whether it is transparent or suspended
  • Modify the screen orientation of the activity, not fixed, and bypass the check.
public class ActivityHook {


    /**
     * java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
     * <p>
     * 修复android 8.0存在的问题
     * <p>
     * 在Activity中onCreate()中super之前调用
     *
     * @param activity
     */
    public static void hookOrientation(Activity activity) {
        //目标版本8.0及其以上
        if (activity.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
            if (isTranslucentOrFloating(activity)) {
                fixOrientation(activity);
            }
        }
    }

    /**
     * 设置屏幕不固定,绕过检查
     *
     * @param activity
     */
    private static void fixOrientation(Activity activity) {
        try {
            Class<Activity> activityClass = Activity.class;
            Field mActivityInfoField = activityClass.getDeclaredField("mActivityInfo");
            mActivityInfoField.setAccessible(true);
            ActivityInfo activityInfo = (ActivityInfo) mActivityInfoField.get(activity);
            //设置屏幕不固定
            activityInfo.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 检查屏幕 横竖屏或者锁定就是固定
     *
     * @param activity
     * @return
     */
    private static boolean isTranslucentOrFloating(Activity activity) {
        boolean isTranslucentOrFloating = false;
        try {
            Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
            Field WindowField = styleableClass.getDeclaredField("Window");
            WindowField.setAccessible(true);
            int[] styleableRes = (int[]) WindowField.get(null);
            //先获取到TypedArray
            final TypedArray typedArray = activity.obtainStyledAttributes(styleableRes);
            Class<?> ActivityInfoClass = ActivityInfo.class;
            //调用检查是否屏幕旋转
            Method isTranslucentOrFloatingMethod = ActivityInfoClass.getDeclaredMethod("isTranslucentOrFloating", TypedArray.class);
            isTranslucentOrFloatingMethod.setAccessible(true);
            isTranslucentOrFloating = (boolean) isTranslucentOrFloatingMethod.invoke(null, typedArray);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isTranslucentOrFloating;
    }


}

Called in oncreate() in the required Activiy:

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
         
         ActivityHook.hookOrientation(this);//hook,绕过检查
         
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_xxxx);

    }

Guess you like

Origin blog.csdn.net/hexingen/article/details/85387011