Android系统 添加动态控制屏幕方向、强制APP横竖屏方向

简介: Android系统 添加动态控制屏幕方向、强制APP横竖屏方向

实现步骤

  • 屏幕旋转功能可以让用户选择屏幕的默认方向,包括0度(竖屏)、90度(横屏)、180度(反向竖屏)和270度(反向横屏)。
  • 强制应用旋转功能可以让用户强制所有应用以横屏或竖屏的方式显示,无论应用本身是否支持旋转。
  • 修改都支持重启后保存哦,强制APP旋转优先级>系统方向优先级。

修改示例

DisplayRotation模块

frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

这个模块负责处理屏幕旋转的逻辑,需要在其中添加1个系统属性:persist.sys.app.rotation,分别用于控制强制应用旋转和屏幕旋转的设置。

在rotationForOrientation()和updateOrientation()方法中,需要根据persist.sys.app.rotation的值来修改当前应用的方向,如果是force_landscape,则强制为横屏;如果是force_portrait,则强制为竖屏;否则按照应用本身的方向设置。

+++ b/frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -380,6 +380,12 @@ public class DisplayRotation {
         if (newOrientation != mCurrentAppOrientation) {
             mCurrentAppOrientation = newOrientation;
             String rot = SystemProperties.get("persist.sys.app.rotation", "middle_port");
+            if (rot.equals("force_landscape")){
+                mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+            }else if (rot.equals("force_portrait")){
+                mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
+                
+            }
             if (rot.equals("force_land") && "box".equals(SystemProperties.get("ro.target.product")))
                 mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
             if (isDefaultDisplay) {
@@ -1204,6 +1210,13 @@ public class DisplayRotation {
             Slog.v(TAG, "asx force_land :" + mLandscapeRotation);
             return mLandscapeRotation;
         }
+        
+        if (rot.equals("force_landscape")){
+            return mLandscapeRotation;
+        }else if (rot.equals("force_portrait")){
+            return mPortraitRotation;
+        }
+        
         switch (orientation) {
             case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                 // Return portrait unless overridden.

Settings模块

packages/apps/Settings/res/values/arrays.xml

这个文件定义了屏幕旋转和强制应用旋转的选项列表,需要在其中添加两个数组:screen_rotate_entries和screen_rotate_values,以及forceapp_rotate_entries和forceapp_rotate_values。

screen_rotate_entries和screen_rotate_values分别表示屏幕旋转的显示名称和对应的值,包括0度、90度、180度和270度。

  • forceapp_rotate_entries和forceapp_rotate_values分别表示强制应用旋转的显示名称和对应的值,包括默认、横屏和竖屏。
+++ b/packages/apps/Settings/res/values-zh-rCN/arrays.xml
@@ -20,6 +20,45 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    
+ 
+    <string-array name="screen_totate_entries">
+        <item>0 Degree</item>
+        <item>90 Degree</item>
+        <item>180 Degree</item>
+        <item>270 Degree</item>
+    </string-array>
+
+    <!-- Do not translate. -->
+    <string-array name="screen_rotate_values" translatable="false">
+        <!-- Do not translate. -->
+        <item>0</item>
+        <!-- Do not translate. -->
+        <item>1</item>
+        <!-- Do not translate. -->
+        <item>2</item>
+        <!-- Do not translate. -->
+        <item>3</item>
+    </string-array>
+    
+         <string-array name="forceapp_rotate_entries">
+        <item>default</item>
+        <item>portrait</item>
+        <item>landscape</item>
+
+  
+    </string-array>
+
+    <!-- Do not translate. -->
+    <string-array name="forceapp_rotate_values" translatable="false">
+        <!-- Do not translate. -->
+        <item>0</item>
+        <!-- Do not translate. -->
+        <item>1</item>
+        <!-- Do not translate. -->
+        <item>2</item>
+
+    </string-array>
packages/apps/Settings/res/values/strings.xml

这个文件定义了英文版的字符串资源,需要在其中添加屏幕旋转和强制应用旋转的标题和摘要,以及对应的格式化字符串

+++ b/packages/apps/Settings/res/values/strings.xml
@@ -14,6 +14,10 @@
      limitations under the License.
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="forceapp_rotate_summary"> <xliff:g id="forceapprotate_description">%1$s</xliff:g> </string>
+    <string name="screen_rotate_summary"> <xliff:g id="screenrotate_description">%1$s</xliff:g> </string>
+    <string name="ctrl_forceapp_rotate" >"Force App Rotate"</string>
+    <string name="ctrl_screen_rotate">"Screen Rotate"</string>
     <string name="ctrl_statusbar">StatusBar</string>
     <string name="ctrl_explan">ExPlan</string>
     <string name="ctrl_navigationbar">NavigationBar</string>
packages/apps/Settings/res/values-zh-rCN/strings.xml

这个文件定义了中文版的字符串资源,需要在其中添加屏幕旋转和强制应用旋转的标题和摘要,以及对应的格式化字符串。

+++ b/packages/apps/Settings/res/values-zh-rCN/strings.xml
@@ -16,6 +16,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="forceapp_rotate_summary"> <xliff:g id="forceapprotate_description">%1$s</xliff:g> </string>
+    <string name="screen_rotate_summary"> <xliff:g id="screenrotate_description">%1$s</xliff:g> </string>
+    <string name="ctrl_forceapp_rotate" >"APP旋转"</string>
+    <string name="ctrl_screen_rotate">"屏幕旋转"</string>
     <string name="ctrl_statusbar">状态栏</string>
     <string name="ctrl_explan">下拉菜单</string>
     <string name="ctrl_navigationbar">导航栏</string>
packages/apps/Settings/res/xml/display_settings.xml

这个文件定义了显示设置的界面布局,添加屏幕旋转和强制应用旋转的列表偏好,以及对应的键值、标题、摘要和选项。

我们使用自定义的ScreenRotateListPreference和ForceAppRotateListPreference类来实现列表偏好的功能,这两个类继承了RestrictedListPreference类,并重写了一些方法来处理管理员限制和对话框显示。

+++ b/packages/apps/Settings/res/xml/display_settings.xml
@@ -31,7 +31,18 @@
    <SwitchPreference
        android:key="ctrl_explan"
        android:title="@string/ctrl_explan"/>
-       
+   <com.android.settings.display.ScreenRotateListPreference
+        android:key="screen_rotate"
+        android:title="@string/ctrl_screen_rotate"
+        android:summary="@string/summary_placeholder"
+        android:entries="@array/screen_rotate_entries"
+        android:entryValues="@array/screen_rotate_values"/>
+   <com.android.settings.display.ForceAppRotateListPreference 
+        android:key="forceapp_rotate"
+        android:title="@string/ctrl_forceapp_rotate"
+        android:summary="@string/summary_placeholder"
+        android:entries="@array/forceapp_rotate_entries"
+        android:entryValues="@array/forceapp_rotate_values"/>
     <com.android.settingslib.RestrictedPreference
         android:key="brightness"
         android:title="@string/brightness"
packages/apps/Settings/src/com/android/settings/DisplaySettings.java

这个文件定义了显示设置的控制器,我们需要在其中添加屏幕旋转和强制应用旋转的偏好控制器,以及对应的键值和上下文。

使用自定义的ScreenRotatePreferenceController和ForceAppRotatePreferenceController类来实现偏好控制器的功能,这两个类继承了AbstractPreferenceController类,并实现了Preference.OnPreferenceChangeListener接口,用于处理偏好变化的事件。

+++ b/packages/apps/Settings/src/com/android/settings/DisplaySettings.java
@@ -40,6 +40,8 @@ import com.android.settingslib.search.SearchIndexable;
 import com.android.settings.display.StatusBarPreferenceController;
 import com.android.settings.display.NavigationBarPreferenceController;
 import com.android.settings.display.ExPlanPreferenceController;
+import com.android.settings.display.ForceAppRotatePreferenceController;
+import com.android.settings.display.ScreenRotatePreferenceController;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -98,6 +100,8 @@ public class DisplaySettings extends DashboardFragment {
         controllers.add(new StatusBarPreferenceController(context));
         controllers.add(new NavigationBarPreferenceController(context));
         controllers.add(new ExPlanPreferenceController(context));
+        controllers.add(new ForceAppRotatePreferenceController(context,"forceapp_rotate"));
+        controllers.add(new ScreenRotatePreferenceController(context,"screen_rotate"));
         return controllers;
     }
packages/apps/Settings/src/com/android/settings/display/ScreenRotateListPreference.java
  • 这个文件定义了屏幕旋转的列表偏好类,需要在其中实现以下功能:
  • 继承RestrictedListPreference类,并在构造方法中初始化初始的选项和值。
  • 重写onPrepareDialogBuilder()方法,在对话框中添加管理员限制的视图,如果有的话。
  • 重写onDialogCreated()方法,在对话框中添加管理员限制的点击事件,如果有的话。
  • 定义一个removeUnusableRotates()方法,用于移除不可用的选项,并根据管理员限制来禁用或启用偏好。
package com.android.settings.display;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import android.content.Intent;
import android.util.Log;
import com.android.settings.R;
import android.os.SystemProperties;
public class ScreenRotatePreferenceController extends AbstractPreferenceController implements
        PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
    private static final String TAG = "ScreenRotatePrefContr";
    /** If there is no setting in the provider, use this. */
    public static final int FALLBACK_SCREEN_ROTATE_VALUE = 0;
    private final String mScreenRotateKey;
    public ScreenRotatePreferenceController(Context context, String key) {
        super(context);
        mScreenRotateKey = key;
    }
    @Override
    public boolean isAvailable() {
        return true;
    }
    @Override
    public String getPreferenceKey() {
        return mScreenRotateKey;
    }
    @Override
    public void updateState(Preference preference) {
        final ScreenRotateListPreference screenRotateListPreference = (ScreenRotateListPreference) preference;
        long currentRotate = Settings.System.getLong(mContext.getContentResolver(),
                Settings.System.USER_ROTATION, FALLBACK_SCREEN_ROTATE_VALUE);
        screenRotateListPreference.setValue(String.valueOf(currentRotate));
        updateRotatePreferenceDescription(screenRotateListPreference, currentRotate);
    }
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        try {
            int value = Integer.parseInt((String) newValue);
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.USER_ROTATION, value);
            updateRotatePreferenceDescription((ScreenRotateListPreference) preference, value);
        } catch (NumberFormatException e) {
            Log.e(TAG, "could not persist screen rotate setting", e);
        }
        return true;
    }
    public static CharSequence getRotateDescription(
            long currentRotate, CharSequence[] entries, CharSequence[] values) {
        if (currentRotate < 0 || entries == null || values == null
                || values.length != entries.length) {
            return null;
        }
        for (int i = 0; i < values.length; i++) {
            long rotate = Long.parseLong(values[i].toString());
            if (currentRotate == rotate) {
                return entries[i];
            }
        }
        return null;
    }
    private void updateRotatePreferenceDescription(ScreenRotateListPreference preference,
            long currentRotate) {
        final CharSequence[] entries = preference.getEntries();
        final CharSequence[] values = preference.getEntryValues();
        final String summary;
        if (preference.isDisabledByAdmin()) {
            summary = mContext.getString(com.android.settings.R.string.disabled_by_policy_title);
        } else {
            final CharSequence rotateDescription = getRotateDescription(
                    currentRotate, entries, values);
            summary = rotateDescription == null
                    ? ""
                    : mContext.getString(R.string.screen_rotate_summary, rotateDescription);
        }
        preference.setSummary(summary);
    }
}
packages/apps/Settings/src/com/android/settings/display/ForceAppRotateListPreference.java
  • 这个文件定义了强制应用旋转的列表偏好类,需要在其中实现以下功能:
  • 继承RestrictedListPreference类,并在构造方法中初始化初始的选项和值。
  • 重写onPrepareDialogBuilder()方法,在对话框中添加管理员限制的视图,如果有的话。
  • 重写onDialogCreated()方法,在对话框中添加管理员限制的点击事件,如果有的话。
  • 定义一个removeUnusableRotates()方法,用于移除不可用的选项,并根据管理员限制来禁用或启用偏好。
package com.android.settings.display;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.core.AbstractPreferenceController;
import android.app.Dialog;
import java.util.ArrayList;
import android.view.View;
import androidx.appcompat.app.AlertDialog;
import android.util.AttributeSet;
import com.android.settings.R;
import com.android.settings.RestrictedListPreference;
import android.content.DialogInterface;
public class ForceAppRotateListPreference extends RestrictedListPreference {
    private EnforcedAdmin mAdmin;
    private final CharSequence[] mInitialEntries;
    private final CharSequence[] mInitialValues;
    public ForceAppRotateListPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        mInitialEntries = getEntries();
        mInitialValues = getEntryValues();
    }
    @Override
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
                                          DialogInterface.OnClickListener listener) {
        super.onPrepareDialogBuilder(builder, listener);
        if (mAdmin != null) {
            builder.setView(R.layout.admin_disabled_other_options_footer);
        } else {
            builder.setView(null);
        }
    }
    @Override
    protected void onDialogCreated(Dialog dialog) {
        super.onDialogCreated(dialog);
        dialog.create();
        if (mAdmin != null) {
            View footerView = dialog.findViewById(R.id.admin_disabled_other_options);
            footerView.findViewById(R.id.admin_more_details_link).setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                           // getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                            RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
                                    getContext(), mAdmin);
                        }
                    });
        }
    }
    public void removeUnusableRotates(long maxRotate, EnforcedAdmin admin) {
        final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
                Context.DEVICE_POLICY_SERVICE);
        if (dpm == null) {
            return;
        }
        if (admin == null && mAdmin == null && !isDisabledByAdmin()) {
            return;
        }
        if (admin == null) {
            maxRotate = Long.MAX_VALUE;
        }
        ArrayList<CharSequence> revisedEntries = new ArrayList<CharSequence>();
        ArrayList<CharSequence> revisedValues = new ArrayList<CharSequence>();
        for (int i = 0; i < mInitialValues.length; ++i) {
            long rotate = Long.parseLong(mInitialValues[i].toString());
            if (rotate <= maxRotate) {
                revisedEntries.add(mInitialEntries[i]);
                revisedValues.add(mInitialValues[i]);
            }
        }
        // If there are no possible options for the user, then set this preference as disabled
        // by admin, otherwise remove the padlock in case it was set earlier.
        if (revisedValues.size() == 0) {
            setDisabledByAdmin(admin);
            return;
        } else {
            setDisabledByAdmin(null);
        }
        if (revisedEntries.size() != getEntries().length) {
            final int userPreference = Integer.parseInt(getValue());
            setEntries(revisedEntries.toArray(new CharSequence[0]));
            setEntryValues(revisedValues.toArray(new CharSequence[0]));
            mAdmin = admin;
            if (userPreference <= maxRotate) {
                setValue(String.valueOf(userPreference));
            } else if (revisedValues.size() > 0
                    && Long.parseLong(revisedValues.get(revisedValues.size() - 1).toString())
                            == maxRotate) {
                // If the last one happens to be the same as the max rotate, select that
                setValue(String.valueOf(maxRotate));
            } else {
                // There will be no highlighted selection since nothing in the list matches
                // maxRotate. The user can still select anything less than maxRotate.
                // TODO: maybe append maxRotate to the list and mark selected.
            }
        }
    }
}
packages/apps/Settings/src/com/android/settings/display/ForceAppRotatePreferenceController.java
  • 这个文件定义了强制应用旋转的偏好控制器类,需要在其中实现以下功能:
  • 继承AbstractPreferenceController类,并实现Preference.OnPreferenceChangeListener接口,用于处理偏好变化的事件。
  • 在构造方法中初始化键值和上下文。
  • 在isAvailable()方法中返回true,表示该控制器可用。
  • 在getPreferenceKey()方法中返回键值。
  • 在updateState()方法中根据系统属性persist.sys.app.rotation的值来更新偏好的选项和摘要。
  • 在onPreferenceChange()方法中根据用户选择的值来修改系统属性persist.sys.app.rotation,并更新偏好的摘要。
  • 定义一个getRotateDescription()方法,用于根据当前的值和选项列表来获取对应的描述。
package com.android.settings.display;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import android.content.Intent;
import android.util.Log;
import com.android.settings.R;
import android.os.SystemProperties;
public class ScreenRotatePreferenceController extends AbstractPreferenceController implements
        PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
    private static final String TAG = "ScreenRotatePrefContr";
    /** If there is no setting in the provider, use this. */
    public static final int FALLBACK_SCREEN_ROTATE_VALUE = 0;
    private final String mScreenRotateKey;
    public ScreenRotatePreferenceController(Context context, String key) {
        super(context);
        mScreenRotateKey = key;
    }
    @Override
    public boolean isAvailable() {
        return true;
    }
    @Override
    public String getPreferenceKey() {
        return mScreenRotateKey;
    }
    @Override
    public void updateState(Preference preference) {
        final ScreenRotateListPreference screenRotateListPreference = (ScreenRotateListPreference) preference;
        long currentRotate = Settings.System.getLong(mContext.getContentResolver(),
                Settings.System.USER_ROTATION, FALLBACK_SCREEN_ROTATE_VALUE);
        screenRotateListPreference.setValue(String.valueOf(currentRotate));
        updateRotatePreferenceDescription(screenRotateListPreference, currentRotate);
    }
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        try {
            int value = Integer.parseInt((String) newValue);
            Settings.System.putInt(mContext.getContentResolver(), Settings.System.USER_ROTATION, value);
            updateRotatePreferenceDescription((ScreenRotateListPreference) preference, value);
        } catch (NumberFormatException e) {
            Log.e(TAG, "could not persist screen rotate setting", e);
        }
        return true;
    }
    public static CharSequence getRotateDescription(
            long currentRotate, CharSequence[] entries, CharSequence[] values) {
        if (currentRotate < 0 || entries == null || values == null
                || values.length != entries.length) {
            return null;
        }
        for (int i = 0; i < values.length; i++) {
            long rotate = Long.parseLong(values[i].toString());
            if (currentRotate == rotate) {
                return entries[i];
            }
        }
        return null;
    }
    private void updateRotatePreferenceDescription(ScreenRotateListPreference preference,
            long currentRotate) {
        final CharSequence[] entries = preference.getEntries();
        final CharSequence[] values = preference.getEntryValues();
        final String summary;
        if (preference.isDisabledByAdmin()) {
            summary = mContext.getString(com.android.settings.R.string.disabled_by_policy_title);
        } else {
            final CharSequence rotateDescription = getRotateDescription(
                    currentRotate, entries, values);
            summary = rotateDescription == null
                    ? ""
                    : mContext.getString(R.string.screen_rotate_summary, rotateDescription);
        }
        preference.setSummary(summary);
    }
}
packages/apps/Settings/src/com/android/settings/display/ScreenRotateListPreference.java
  • 这个文件定义了屏幕旋转的列表偏好类,需要在其中实现以下功能:
  • 继承RestrictedListPreference类,并在构造方法中初始化初始的选项和值。
  • 重写onPrepareDialogBuilder()方法,在对话框中添加管理员限制的视图,如果有的话。
  • 重写onDialogCreated()方法,在对话框中添加管理员限制的点击事件,如果有的话。
  • 定义一个removeUnusableRotates()方法,用于移除不可用的选项,并根据管理员限制来禁用或启用偏好。
package com.android.settings.display;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import android.content.Intent;
import android.util.Log;
import com.android.settings.R;
import android.os.SystemProperties;
public class ForceAppRotatePreferenceController extends AbstractPreferenceController implements
        PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
    private static final String TAG = "ForceAppRotatePrefContr";
    /** If there is no setting in the provider, use this. */
    public static final int FALLBACK_FORCE_APP_ROTATE_VALUE = 0;
    private final String mForceAppRotateKey;
    public ForceAppRotatePreferenceController(Context context, String key) {
        super(context);
        mForceAppRotateKey = key;
    }
    @Override
    public boolean isAvailable() {
        return true;
    }
    @Override
    public String getPreferenceKey() {
        return mForceAppRotateKey;
    }
    @Override
    public void updateState(Preference preference) {
        final ForceAppRotateListPreference forceAppRotateListPreference = (ForceAppRotateListPreference) preference;
        long currentRotate = 0;
        String rot = SystemProperties.get("persist.sys.app.rotation", "middle_port");
        if (rot.equals("force_landscape")){
            currentRotate = 1;
        }else if (rot.equals("force_portrait")){
            currentRotate = 2;
        }else{
            currentRotate = 0;
        }
        
        forceAppRotateListPreference.setValue(String.valueOf(currentRotate));
        updateRotatePreferenceDescription(forceAppRotateListPreference, currentRotate);
    }
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        try {
            int value = Integer.parseInt((String) newValue);
            if(value==0){    
                SystemProperties.set("persist.sys.app.rotation", "");
            }else if (value==1){
                SystemProperties.set("persist.sys.app.rotation", "force_portrait");
            }else if (value==2){
                SystemProperties.set("persist.sys.app.rotation", "force_landscape");
            }
            //Settings.System.putInt(mContext.getContentResolver(), "FORCE_APP_ROTATION", value);
            updateRotatePreferenceDescription((ForceAppRotateListPreference) preference, value);
        } catch (NumberFormatException e) {
            Log.e(TAG, "could not persist force app rotate setting", e);
        }
        return true;
    }
    public static CharSequence getRotateDescription(
            long currentRotate, CharSequence[] entries, CharSequence[] values) {
        if (currentRotate < 0 || entries == null || values == null
                || values.length != entries.length) {
            return null;
        }
        for (int i = 0; i < values.length; i++) {
            long rotate = Long.parseLong(values[i].toString());
            if (currentRotate == rotate) {
                return entries[i];
            }
        }
        return null;
    }
    private void updateRotatePreferenceDescription(ForceAppRotateListPreference preference,
            long currentRotate) {
        final CharSequence[] entries = preference.getEntries();
        final CharSequence[] values = preference.getEntryValues();
        final String summary;
        if (preference.isDisabledByAdmin()) {
            summary = mContext.getString(com.android.settings.R.string.disabled_by_policy_title);
        } else {
            final CharSequence rotateDescription = getRotateDescription(
                    currentRotate, entries, values);
            summary = rotateDescription == null
                    ? ""
                    : mContext.getString(R.string.forceapp_rotate_summary, rotateDescription);
        }
        preference.setSummary(summary);
    }
}

OK 到这里就全部添加完成 没有难度~

其他记录:

  1. 比如ForceAppRotateListPreference文件为什么要新建这个东西? 早期版本没这么麻烦
  2. 有个坑xxxListPreference里面的AlertDialog11+版本要用androidx.appcompat.app.AlertDialog; 不能用早期版本的 , 如果同时导入2个包 会一直编译报错 只需保留一个就可以。

测试效果:

不管应用是否支持竖屏 , 都会被强制竖屏。

总结:

本文介绍了如何在Android系统Settings中添加屏幕旋转和强制App应用旋转的功能,以便客户可以根据项目需求调整屏幕方向

相关文章
|
2月前
|
存储 SQL 虚拟化
Omnissa App Volumes 4, version 2503 - 实时应用程序交付系统
Omnissa App Volumes 4, version 2503 - 实时应用程序交付系统
63 7
Omnissa App Volumes 4, version 2503 - 实时应用程序交付系统
|
21天前
|
Android开发 数据安全/隐私保护 开发者
Android自定义view之模仿登录界面文本输入框(华为云APP)
本文介绍了一款自定义输入框的实现,包含静态效果、hint值浮动动画及功能扩展。通过组合多个控件完成界面布局,使用TranslateAnimation与AlphaAnimation实现hint文字上下浮动效果,支持密码加密解密显示、去除键盘回车空格输入、光标定位等功能。代码基于Android平台,提供完整源码与attrs配置,方便复用与定制。希望对开发者有所帮助。
|
4月前
|
JavaScript 前端开发 Android开发
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
146 13
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
22天前
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
93 3
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
2月前
|
数据采集 JSON 网络安全
移动端数据抓取:Android App的TLS流量解密方案
本文介绍了一种通过TLS流量解密技术抓取知乎App热榜数据的方法。利用Charles Proxy解密HTTPS流量,分析App与服务器通信内容;结合Python Requests库模拟请求,配置特定请求头以绕过反爬机制。同时使用代理IP隐藏真实IP地址,确保抓取稳定。最终成功提取热榜标题、内容简介、链接等信息,为分析热点话题和用户趋势提供数据支持。此方法也可应用于其他Android App的数据采集,但需注意选择可靠的代理服务。
113 11
移动端数据抓取:Android App的TLS流量解密方案
|
1月前
|
移动开发 缓存 开发框架
轻型社交同城交友圈子系统/兴趣爱好搭子聊天APP/同城本地行业信息圈子论坛
基于 UniApp 和 ThinkPHP6 构建,实现多端同步的轻量级社交系统。前端采用 UniApp 跨端开发框架,支持微信小程序、H5、APP 等多平台,结合 Vue.js 语法与图鸟 UI 组件库,快速构建美观界面。后端使用 TP6 提供 RESTful API,搭配 MySQL 数据库与 Redis 缓存优化性能。核心功能包括兴趣圈子管理、即时通讯、付费圈子、广告与会员体系等。同时,通过七牛云内容检测与实名认证保障社区安全,采用 Nginx+Redis 高并发架构确保稳定性。免费源码,适合开发者快速搭建同城社交平台,并可通过 AI 推荐与 AR 功能进一步提升用户体验。
155 5
|
2月前
|
安全 搜索推荐 Android开发
Android系统SELinux安全机制详解
如此看来,SELinux对于大家来说,就像那位不眠不休,严阵以待的港口管理员,守护我们安卓系统的平安,维护这片海港的和谐生态。SELinux就这样,默默无闻,却卫士如山,给予Android系统一份厚重的安全保障。
129 18
|
3月前
|
存储 文件存储 Android开发
仿第八区APP分发下载打包封装系统源码
该系统为仿第八区APP分发下载打包封装系统源码,支持安卓、iOS及EXE程序分发,自动判断并稳定安装。智能提取应用信息,自动生成PLIST文件和图标,提供合理的点数扣除机制。支持企业签名在线提交、专属下载页面生成、云端存储(阿里云、七牛云),并优化签名流程,支持中文包及合并分发,确保高效稳定的下载体验。 [点击查看源码](https://download.csdn.net/download/huayula/90463452)
242 22
|
3月前
|
存储 JavaScript
(ERP系统查看DWG)MxCAD APP调用内部弹框的方法
MxCAD APP 二次开发提供了调用项目内部弹框的接口,以保持样式统一。用户需创建 `test_dialog` 文件夹并依次创建 `dialog.ts`、`dialog.vue` 和 `index.ts` 文件来注册、构建和渲染弹框。通过 `useDialogIsShow` 钩子函数控制弹框显示,并可在方法中直接调用 `dialog.showDialog()` 来控制弹框显隐。此外,还支持监听确认或取消事件获取数据,以及通过配置 `vite.config.ts` 解决样式冲突问题。最终在 `src/index.ts` 中引入相关文件即可实现弹框功能。
|
4月前
|
缓存
flutter3-wetrip跨平台自研仿携程app预约酒店系统模板
基于最新跨平台框架flutter3.x+dart3+getx+flutter_datepicker纯手写实战的一款仿去哪儿/携程旅游酒店预约客房app系统。
118 8

热门文章

最新文章