android framework13-launcher3【

1.简介

平板模式没啥问题,手机模式的话,桌面默认是不可以旋转的,打开桌面旋转开关以后,也只能横向旋转,180度那个是没效果的,下边就具体找下原因,看看是哪里做了限制

注意:我们这里分析手机模式不能旋转的问题。

这里贴下平板和手机判断的逻辑,取设备宽和高里比较小的值,计算其dp值,比600大的是认为是tablet,否则就是phone

    public static final int DENSITY_MEDIUM = 160;
    public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;
    //像素转化为DP值
    public static float dpiFromPx(float size, int densityDpi) {
        float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
        return (size / densityRatio);
    }

参考:

blog.csdn.net/sgmenghuo/a…

2.桌面设置页面

2.1 打开桌面自动旋转

桌面空白处长按,弹出的菜单选择 home settings,然后就能看到桌面的设置页面了,那个allow home screen rotation就是旋转开关,手机模式默认是关闭的,平板模式这个偏好隐藏了。

image.png

2.2.布局相关

>清单文件

        The settings activity. To extend point settings_fragment_name to appropriate fragment class
        -->
        <activity
            android:name="com.android.launcher3.settings.SettingsActivity"
            android:label="@string/settings_button_text"
            android:theme="@style/HomeSettings.Theme"
            android:exported="true"
            android:autoRemoveFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

>settings_activity.xml

可以看到,就一个toolbar显示标题,完事就是个帧布局,到时候替换成fragment了

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <Toolbar
        android:id="@+id/action_bar"
        style="?android:attr/actionBarStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="?android:attr/actionBarTheme" />

    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

>launcher_preferences.xml

fragment用到的preference文件如下,

<androidx.preference.PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res-auto">

    <com.android.launcher3.settings.NotificationDotsPreference
        android:key="pref_icon_badging"
        android:title="@string/notification_dots_title"
        android:persistent="false"
        android:widgetLayout="@layout/notification_pref_warning" />

    <!--
      LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED(613)
      LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_DISABLED(614)
    -->
    <SwitchPreference
        android:key="pref_add_icon_to_home"
        android:title="@string/auto_add_shortcuts_label"
        android:summary="@string/auto_add_shortcuts_description"
        android:defaultValue="true"
        android:persistent="true"
        launcher:logIdOn="613"
        launcher:logIdOff="614" />

    <!--
      LAUNCHER_HOME_SCREEN_ROTATION_ENABLED(615)
      LAUNCHER_HOME_SCREEN_ROTATION_DISABLED(616)
    -->
    <SwitchPreference
        android:key="pref_allowRotation"
        android:title="@string/allow_rotation_title"
        android:summary="@string/allow_rotation_desc"
        android:defaultValue="false"
        android:persistent="true"
        launcher:logIdOn="615"
        launcher:logIdOff="616" />

    <androidx.preference.PreferenceScreen
        android:key="pref_developer_options"
        android:persistent="false"
        android:title="@string/developer_options_title"
        android:fragment="com.android.launcher3.settings.DeveloperOptionsFragment"/>

</androidx.preference.PreferenceScreen>

2.3.SettingsActivity

>onCreate

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.settings_activity);
        setActionBar(findViewById(R.id.action_bar));
//..
        if (savedInstanceState == null) {
//..
            final FragmentManager fm = getSupportFragmentManager();
            final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
            //获取要加载的fragment
                    getPreferenceFragment());
            f.setArguments(args);
            // 替换成fragment
            fm.beginTransaction().replace(R.id.content_frame, f).commit();
        }

>getPreferenceFragment

<string name="settings_fragment_name" translatable="false">
com.android.launcher3.settings.SettingsActivity$LauncherSettingsFragment</string>
    private String getPreferenceFragment() {
        String preferenceFragment = getIntent().getStringExtra(EXTRA_FRAGMENT);
        //我们这里用的是默认的这个
        String defaultFragment = getString(R.string.settings_fragment_name);

        if (TextUtils.isEmpty(preferenceFragment)) {
            return defaultFragment;
        } else if (!preferenceFragment.equals(defaultFragment)
                && !VALID_PREFERENCE_FRAGMENTS.contains(preferenceFragment)) {
            如果是extra传递的,需要验证是否在注册的集合里
            throw new IllegalArgumentException(
                    "Invalid fragment for this activity: " + preferenceFragment);
        } else {
            return preferenceFragment;
        }
    }

2.4.LauncherSettingsFragment

默认加载的就是这个Fragment

>onCreatePreferences

    public static class LauncherSettingsFragment extends PreferenceFragmentCompat {
//...

   public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
//..
            getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
            //设置偏好资源
            setPreferencesFromResource(R.xml.launcher_preferences, rootKey);
            PreferenceScreen screen = getPreferenceScreen();
            for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
                Preference preference = screen.getPreference(i);
                //对偏好进行初始化
                if (initPreference(preference)) {
                    
                } else {
                //初始化失败的话移除选项
                    screen.removePreference(preference);
                }
            }
            

>initPreference

        protected boolean initPreference(Preference preference) {
            switch (preference.getKey()) {
                case NOTIFICATION_DOTS_PREFERENCE_KEY:
                //注意:这个类在src_shortcuts_overrides目录下重写了,值改成了false,所以返回的是true
                    return !WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS;

                case ALLOW_ROTATION_PREFERENCE_KEY:
                    DisplayController.Info info =
                            DisplayController.INSTANCE.get(getContext()).getInfo();
                    if (info.isTablet(info.realBounds)) {
                      //平板模式支持自动旋转,所以这里返回false,移除选项
                        return false;
                    }
                    // 非平板模式,默认值是false
                    preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info));
                    return true;

                case FLAGS_PREFERENCE_KEY:
                    // Only show flag toggler UI if this build variant implements that.
                    return FeatureFlags.showFlagTogglerUi(getContext());

                case DEVELOPER_OPTIONS_KEY:
                    mDeveloperOptionPref = preference;
                    return updateDeveloperOption();
            }

            return true;
        }

3.RotationHelper.java

这个是launcher3里管理桌面旋转的工具类,只是用来控制桌面app能否旋转的,和我们要研究的手机无法旋转180度的问题不相关。

3.1.getAllowRotationDefaultValue

这个就是上边settings里自动旋转开关的默认值

    public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";

    public static boolean getAllowRotationDefaultValue(DisplayController.Info info) {
        float originalSmallestWidth = dpiFromPx(Math.min(info.currentSize.x, info.currentSize.y),
                DENSITY_DEVICE_STABLE);
        return originalSmallestWidth >= MIN_TABLET_WIDTH;//600
    }

DENSITY_DEVICE_STABLE


    //这个是不变的
    public static final int DENSITY_DEVICE_STABLE = getDeviceDensity();
    
    private static int getDeviceDensity() {
        //ro.sf.lcd_density的值在初始化进程的时候从build.prop里读取写入一次,之后不会修改
        //qemu.sf.lcd_density覆写上边的值,目的是在模拟器的时候可以动态修改
        return SystemProperties.getInt("qemu.sf.lcd_density",
                SystemProperties.getInt("ro.sf.lcd_density", DENSITY_DEFAULT));
    }
    
ublic static final int DENSITY_DEFAULT = DENSITY_MEDIUM;//160    

3.2.initialize

    public void initialize() {
        if (!mInitialized) {
            mInitialized = true;
            DisplayController displayController = DisplayController.INSTANCE.get(mActivity);
            DisplayController.Info info = displayController.getInfo();
            //可以看到第一个参数,平板的话为true,忽略自动旋转设置
            setIgnoreAutoRotateSettings(info.isTablet(info.realBounds), info);
            //添加设备info改变监听
            displayController.addChangeListener(this);
            notifyChange();
        }
    }

>setIgnoreAutoRotateSettings

    private void setIgnoreAutoRotateSettings(boolean ignoreAutoRotateSettings,
            DisplayController.Info info) {
        // On large devices we do not handle auto-rotate differently.
        mIgnoreAutoRotateSettings = ignoreAutoRotateSettings;
        if (!mIgnoreAutoRotateSettings) {
            //非平板
            if (mSharedPrefs == null) {
                mSharedPrefs = LauncherPrefs.getPrefs(mActivity);
                mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
            }
            //非平板,那么这个就看auto rotation选项开关有没有打开,默认值是false
            mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
                    getAllowRotationDefaultValue(info));
        } else {
            //平板的话不用监听改变
            if (mSharedPrefs != null) {
                mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
                mSharedPrefs = null;
            }
        }
    }

>onDisplayInfoChanged

    public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) {
        boolean ignoreAutoRotateSettings = info.isTablet(info.realBounds);
        if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
         //平板,非平板模式变化的时候重新设置
            setIgnoreAutoRotateSettings(ignoreAutoRotateSettings, info);
            notifyChange();
        }
    }

3.3.notifyChange

简单来讲,就是根据不同的请求,返回activity需要的flag(就是屏幕方向旋转的参数)

  • 这里最终就整合了3种flag
  • SCREEN_ORIENTATION_UNSPECIFIED :旋转方向不限制
  • SCREEN_ORIENTATION_LOCKED :屏幕锁定,不可以旋转
  • SCREEN_ORIENTATION_NOSENSOR :这个也不能旋转了,都设置成没有传感器了
    private void notifyChange() {
        if (!mInitialized || mDestroyed) {
            return;
        }

        final int activityFlags;
        if (mStateHandlerRequest != REQUEST_NONE) {
            activityFlags = mStateHandlerRequest == REQUEST_LOCK ?
                    SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
        } else if (mCurrentTransitionRequest != REQUEST_NONE) {
            activityFlags = mCurrentTransitionRequest == REQUEST_LOCK ?
                    SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
        } else if (mCurrentStateRequest == REQUEST_LOCK) {
            activityFlags = SCREEN_ORIENTATION_LOCKED;
        } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
                || mHomeRotationEnabled || mForceAllowRotationForTesting) {
            activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
        } else {
            // If auto rotation is off, allow rotation on the activity, in case the user is using
            // forced rotation.
            activityFlags = SCREEN_ORIENTATION_NOSENSOR;
        }
        if (activityFlags != mLastActivityFlags) {
            mLastActivityFlags = activityFlags;
            mRequestOrientationHandler.sendEmptyMessage(activityFlags);
        }
    }

>setOrientationAsync

handle最终就是把上边的flag给到activity去请求,设置旋转参数

    private boolean setOrientationAsync(Message msg) {
        Activity activity = mActivity;
        if (activity != null) {
            activity.setRequestedOrientation(msg.what);
        }
        return true;
    }

3.5.build.prop

www.cnblogs.com/myitm/archi…

文件位于手机的/system/目录下,记录一些系统设置,是一个属性文件。

>生成

Make系统解析build/core/Makefile,调用build/tools/buildinfo.sh执行脚本生成build.prop文件,并把系统默认的system.prop以及定制的system.prop中的属性追加到build.prop文件中,编译完成之后,文件生成在out/target/product/[board]/system/目录下

>常用属性说明

# begin build properties    #开始设置系统性能
# autogenerated by buildinfo.sh #以下内容由脚本在编译时自动产生
ro.build.id=JRO03C    #build的标识,一般在编译时产生不必修改
ro.build.display.id=TBDG1073-eng 4.1.1 JRO03C 20130723.v016 test-keys   #显示的标识,可以任意修改,显示为手机信息的版本
ro.build.version.incremental=20130723.v016  #版本的增加说明,一般不显示也没必要修改
ro.build.version.sdk=16 #系统编译时,使用的SDK的版本,勿修改.
ro.build.version.codename=REL     #版本编码名称,一般不显示也没必要修改
ro.build.version.release=4.1.1    #公布的版本,显示为手机信息的系统版本
ro.build.date=Tue Jul 23 17:14:43 CST 2013   #系统编译的时间,没必要修改
ro.build.date.utc=1374570883     #系统编译的时间(数字版),没必要修改
ro.build.type=eng   #系统编译类型,一般不显示也没必要修改
ro.build.user=pyou  #系统用户名,可以修改成自己的名字
ro.build.host=roco-ubuntu    #系统主机名,随便起个名字,英文字母表示
ro.build.tags=test-keys  #系统标记,无意义,不修改
ro.product.model=TBDG1073_OuyangPeng    #机器型号,随你创造
ro.product.brand=TBDG1073    #机器品牌,随你创造
ro.product.name=TBDG1073     #机器名,随你创造
ro.product.device=TBDG1073   #设备名,随你创造
ro.product.board=TBDG1073    #主板名,随你创造
ro.product.cpu.abi=armeabi-v7a   #CPU,最好别修改,避免有些软件在识别机器时,出现错乱
ro.product.cpu.abi2=armeabi  #CPU品牌
ro.product.manufacturer=TBDG1073     #制造商,随你创造
ro.product.locale.language=en   #系统语言
ro.product.locale.region=US #系统所在地区
ro.wifi.channels=11     #无线局域网络的通信信道,空白表示自动识别
ro.board.platform=meson6    #主板系统
# ro.build.product is obsolete; use ro.product.device
ro.build.product=TBDG1073   #设备名,被废弃了,修改也没用
# Do not try to parse ro.build.description or .fingerprint  #以下的内容不要试图修改
ro.build.description=TBDG1073-eng 4.1.1 JRO03C 20130723.v016 test-keys  #用户的KEY
ro.build.fingerprint=TBDG1073/TBDG1073/TBDG1073:4.1.1/JRO03C/20130723.v016:eng/test-keys  #机身码
ro.build.characteristics=tablet
# end build properties  #创建属性结束
# system.prop for M1 reference board    #系统技术支持由M1提供
# This overrides settings in the products/generic/system.prop file
#
#rild.libpath=/system/lib/libreference-ril.so
#rild.libargs=-d /dev/ttyS0
ro.sf.lcd_density=120 #显示屏分辨率,数值越大分辨率越底
keyguard.no_require_sim=1   #无需SIM卡也可操作手机
#set font
ro.fontScale=1.0    #字体大小缩放
#set keyguard.enable=false to disable keyguard
keyguard.enable=true    #锁屏
ro.statusbar.widget=true
ro.statusbar.button=true
ro.statusbar.yearmonthdayweek=true


#wifi.interface=ra0 #WIFI界面
# Time between scans in seconds. Keep it high to minimize battery drain.
# This only affects the case in which there are remembered access points,
# but none are in range.
#wifi.supplicant_scan_interval = 60 #WIFI扫描间隔时间,这里设置是45秒。把这个时间设置长点能省电
#alsa.mixer.playback.master=DAC2 Analog
#alsa.mixer.capture.master=Analog
#configure the Dalvik heap for a standard tablet device.
#frameworks/base/build/tablet-dalvik-heap.mk
dalvik.vm.heapstartsize=5m  #单个应用程序分配的初始内存
dalvik.vm.heapgrowthlimit=48m   #单个应用程序最大内存限制,超过将被Kill,这或许是某些大体积程序闪退的原因
dalvik.vm.heapsize=256m  #dalvik的虚拟内存大小


hwui.render_dirty_regions=false


# Disable un-supported Android feature
hw.nopm=false
hw.nobattery=false
hw.nophone=true
hw.novibrate=true
hw.cameras=1
hw.hasethernet=false
#hw.hasdata=true
ro.platform.has.touch=true
hw.nodatausage=true
# Wi-Fi sleep policy
ro.platform.has.sleeppolicy=false
#set to 0 temporarily so touch works without other changes
ro.sf.hwrotation=270    #0的话自动转屏
#0~7 You are required to get the correct install direction according the sensor placement on target board
#ro.sf.gsensorposition=6
ro.sf.ecompassposition=4
allow_all_orientations=1




# Set Camera Orientation
ro.camera.orientation.front=270
ro.camera.orientation.back=90


# Use OSD2 mouse patch
ro.ui.cursor=osd2


ro.hardware=amlogic


# Enable 32-bit OSD
sys.fb.bits=32


# Disable GPS
gps.enable=false


# Enable player buildin
media.amsuperplayer.enable=true
media.amplayer.enable-acodecs=asf,ape,flac,dts
media.amplayer.enable=true
media.amsuperplayer.m4aplayer=STAGEFRIGHT_PLAYER
media.amsuperplayer.defplayer=PV_PLAYER
media.amplayer.thumbnail=true
media.amplayer.stopbuflevel=0.05
media.amplayer.widevineenable=true
media.amplayer.html5_stretch=true
media.libplayer.fastswitch=0
media.libplayer.ipv4only=1
media.amplayer.dsource4local=1
#media.amplayer.hdmicloseauthen=1
media.amplayer.delaybuffering=2
media.amplayer.buffertime=5
media.amplayer.v4osd.enable=1
media.arm.audio.decoder=ape
#fix doubleTwist apk can not play radio
media.player.forcemp3softdec=true


#fix online video block issue
libplayer.livets.softdemux=1
libplayer.netts.recalcpts=1


# Nand write need force sync when gadget
gadget.nand.force_sync=true




# Status bar customization
ro.statusbar.widget.power=true
ro.statusbar.yearmonthdayweek=true


# HDMI 
#ro.hdmi480p.enable=true
#rw.fb.need2xscale=ok
#media.amplayer.osd2xenable=true


#camera DCIM dir. 0:sd only; 1:nand only; 2,sd first
ro.camera.dcim=1


# Disable preload-class
ro.amlogic.no.preloadclass=0


# App optimization
ro.app.optimization=true


persist.sys.timezone=America/New_York   #强制时区,此处为美洲纽约时间
#Dual display
ro.vout.dualdisplay3=true
ro.vout.player.exit=false


# CPU settings
ro.has.cpu.setting=true


# CPU freq customized in setting menu
# normal, performance, powersaving
ro.cpumode.maxfreq=1200000,1320000,800000


# when usbstorage, CPU mode and freq
ro.usbstorage.cpumode=performance
ro.usbstorage.maxfreq=600000


ro.bootanimation.rotation=0


#used to set default surface size, set 1 when hwrotation is 270, set 3 when hwrotation is 90;need set ro.bootanimation.rotation 0;
debug.default.dimention=1


#support media poll uevent,can use sd cardread on usb port
has.media.poll=true


#used forward seek for libplayer
media.libplayer.seek.fwdsearch=1


#for tabletui display
ro.ui.tabletui=true
#enable address bar cover issue fixing
ro.flashplayer.surfacehack=1


#add vol button in statusbar.
ro.statusbar.volume=true


ro.screen.has.usbstorage=true
hw.erase.internalSdcard=true


#media partition name
ro.media.partition.label=OuyangPeng


#USB PID and VID name
#ro.usb.vendor.string=AML
#ro.usb.product.string=MID
#CTS
#media.amplayer.widevineenable=true
#media.amplayer.dsource4local=true
ro.com.google.gmsversion=4.1_r5
ro.com.google.clientidbase=android-fih  #谷歌客户身份
ro.setupwizard.mode=OPTIONAL    #安装向导模式 开机出现的帐号设置向导,ENABLED为显示,DISABLED为禁用,OPTIONAL为可选
ro.statusbar.screenshot=true


#
# ADDITIONAL_BUILD_PROPERTIES
#
ro.com.android.dateformat=MM-dd-yyyy     #默认时间格式,改为yyyy-MM-dd,显示效果就是XXXX年XX月XX日
ro.config.ringtone=Ring_Synth_04.ogg     #默认响铃铃声,文件在/system/media/audio/ringtones 把喜欢的铃声放这里
ro.config.notification_sound=pixiedust.ogg  #默认提示音,文件在/system/media/audio/notifications 修改方法同上
ro.carrier=unknown
ro.opengles.version=131072  #开放式绘图介面参数
ro.config.alarm_alert=Alarm_Classic.ogg     #默认闹铃,文件在/system/media/audio/alarms 修改方法同上
drm.service.enabled=true
ro.setupwizard.mode=OPTIONAL #默认开机时使用设置向导
ro.com.google.gmsversion=4.1_r4
ro.kernel.android.checkjni=1
net.bt.name=Android #蓝牙网络中显示的名称,可以修改
dalvik.vm.stack-trace-file=/data/anr/traces.txt

>

4.auto rotate 开关

我们知道快速设置里的旋转开关可以控制整个系统是否可以旋转,所以先看下这个开关状态改变都做了啥?

image.png

4.1.RotationLockTile.java

先找到这个控件的相关类,看下点击事件都干啥了

>getLongClickIntent

长按跳转的intent

    public Intent getLongClickIntent() {
        return new Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS);
    }

>handleClick

点击切换开关状态,开关打开,rotationLock为false,开关关闭,rotationLock为true

    protected void handleClick(@Nullable View view) {
        final boolean newState = !mState.value;
        //controller就是4.2的类,最终实现见 5.1
        mController.setRotationLocked(!newState);
        refreshState(newState);
    }

>handleUpdateState

可以看到开关的状态和旋转锁定是反着的,也就是

  • 旋转锁定的时候,开关是false,
  • 旋转打开的时候,开关是true,
    protected void handleUpdateState(BooleanState state, Object arg) {
        final boolean rotationLocked = mController.isRotationLocked();
        //
        state.value = !rotationLocked;

4.2.RotationLockControllerImpl

简单看下构造方法

    public RotationLockControllerImpl(
            RotationPolicyWrapper rotationPolicyWrapper,
            DeviceStateRotationLockSettingController deviceStateRotationLockSettingController,
            @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
    ) {

可以看到的方法的实现交给了RotationPolicyWrapper

    public void setRotationLocked(boolean locked) {
        mRotationPolicy.setRotationLock(locked);
    }

>RotationPolicyWrapper

而这个RotationPolicyWrapper的实现又都交给了RotationPolicy,所以最终看下 小节5 即可

class RotationPolicyWrapperImpl @Inject constructor(
    private val context: Context,
    private val secureSettings: SecureSettings
) :RotationPolicyWrapper {

    override fun setRotationLock(enabled: Boolean) {
        traceSection("RotationPolicyWrapperImpl#setRotationLock") {
            RotationPolicy.setRotationLock(context, enabled)
        }
    }

    override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) {
        RotationPolicy.setRotationLockAtAngle(context, enabled, rotation)
    }

5.RotationPolicy

这个类里的都是静态方法,一个个的看一下

    private static final int CURRENT_ROTATION = -1;

    public static final int NATURAL_ROTATION = Surface.ROTATION_0;

    private RotationPolicy() {
    }

5.1.setRotationLock

设置是否可以旋转

    public static void setRotationLock(Context context, final boolean enabled) {
    //见 5.2 
        final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
        //见5.3
        setRotationLockAtAngle(context, enabled, rotation);
    }

5.2.areAllRotationsAllowed

是否允许所有角度的旋转,目前 phone的配置里是false,tablet下是true

    private static boolean areAllRotationsAllowed(Context context) {
        return context.getResources().getBoolean(R.bool.config_allowAllRotations);
    }

5.3.setRotationLockAtAngle

设置是否可以旋转以及角度(-1或者0),-1表示所有方向都可以旋转,0表示部分角度可以旋转(排除180度的)

    public static void setRotationLockAtAngle(Context context, final boolean enabled,
            final int rotation) {
            //可以看到,存储的是固定的值 0,表示不隐藏旋转开关
        Settings.System.putIntForUser(context.getContentResolver(),
                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
                UserHandle.USER_CURRENT);

        setRotationLock(enabled, rotation);
    }

>HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY

  • 控制是否隐藏系统UI中的旋转锁定开关。通常,这样做是出于可访问性的目的,使用户在显示旋转被锁定时更难以意外地切换旋转锁定。
  • 如果为0,则不隐藏旋转锁定开关以方便访问(尽管可能由于其他原因而不可用)。
  • 如果为1,则隐藏旋转锁定开关
        public static final String HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY =
                "hide_rotation_lock_toggle_for_accessibility";

5.4.setRotationLock

    private static void setRotationLock(final boolean enabled, final int rotation) {
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
                    if (enabled) {
                        wm.freezeRotation(rotation);
                    } else {
                        wm.thawRotation();
                    }
                } 
            }
        });
    }

>getWindowManagerService

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                //查下这个
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                        sUseBLASTAdapter = sWindowManagerService.useBLAST();
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

>SystemServer.java

startOtherService方法里初始化的,所以上边的"window"对应的就是WindowManagerService

            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);

5.5.getRotationLockOrientation

获取禁止屏幕旋转以后应该显示的方向,横屏或者竖屏

    public static int getRotationLockOrientation(Context context) {
        if (areAllRotationsAllowed(context)) {
        //所有旋转方向都支持,返回0
            return Configuration.ORIENTATION_UNDEFINED;
        }
        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        //旋转角度,0,1,2,3
        final int rotation =
                context.getResources().getConfiguration().windowConfiguration.getRotation();
        final boolean rotated = rotation % 2 != 0;(1或者3表示旋转了,也就是横屏模式)
        final int w = rotated ? metrics.heightPixels : metrics.widthPixels;
        final int h = rotated ? metrics.widthPixels : metrics.heightPixels;
        return w < h ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
    }

6.WindowManagerService.java

这个是中间类,最终交给 小节7处理了

6.1.freezeRotation

    public void freezeRotation(int rotation) {
        freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
    }

>freezeDisplayRotation

    public void freezeDisplayRotation(int displayId, int rotation) {
       //..
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent display = mRoot.getDisplayContent(displayId);
                if (display == null) {
                    return;
                }
                //走这里
                display.getDisplayRotation().freezeRotation(rotation);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        updateRotationUnchecked(false, false);
    }

6.2.thawRotation

    public void thawRotation() {
        thawDisplayRotation(Display.DEFAULT_DISPLAY);
    }

>thawDisplayRotation

    public void thawDisplayRotation(int displayId) {
        //..
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent display = mRoot.getDisplayContent(displayId);
                if (display == null) {
                    return;
                }
                //走这里
                display.getDisplayRotation().thawRotation();
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        updateRotationUnchecked(false, false);
    }

7.DisplayRotation.java

WindowManagerPolicy.java

    @IntDef({USER_ROTATION_FREE, USER_ROTATION_LOCKED})
    @Retention(RetentionPolicy.SOURCE)
    public @interface 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;

7.1.thawRotation

  • mUserRotation 就是屏幕当前的旋转角度
    void thawRotation() {
        setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, mUserRotation);
    }

7.2.freezeRotation

  • mRotation 就是当前屏幕的方向[0,1,2,3]
  • rotation 从5.1传过来的,-1表示可以4个方向旋转,0表示可以3个方向旋转
  • 可以看到,旋转开关关闭,如果是rotation是-1,那么最终的角度还是当前的角度,
  • 而如果rotation是0(旋转受限,180度的不行),这种最终的角度就固定是0了,比如原本是横屏,开关关闭,直接就成竖屏显示了。
    void freezeRotation(int rotation) {
        rotation = (rotation == -1) ? mRotation : rotation;
        setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation);
    }

7.3.setUserRotation

    void setUserRotation(int userRotationMode, int userRotation) {
        if (isDefaultDisplay) {
            // We'll be notified via settings listener, so we don't need to update internal values.
            final ContentResolver res = mContext.getContentResolver();
            final int accelerometerRotation =
                    userRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED ? 0 : 1;
            //禁止旋转的话是0,允许旋转的话是1
            Settings.System.putIntForUser(res, Settings.System.ACCELEROMETER_ROTATION,
                    accelerometerRotation, UserHandle.USER_CURRENT);
            //存储当前屏幕的旋转角度
            Settings.System.putIntForUser(res, Settings.System.USER_ROTATION, userRotation,
                    UserHandle.USER_CURRENT);
            return;
        }

>settings key

        /**
         * Control whether the accelerometer will be used to change screen
         * orientation.  If 0, it will not be used unless explicitly requested
         * by the application; if 1, it will be used by default unless explicitly
         * disabled by the application.
         */
        // 0表示不使用加速度传感器改变屏幕方向,
        // 1表示使用加速度传感器,
        public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation";

        /**
         * Default screen rotation when no other policy applies.
         * When {@link #ACCELEROMETER_ROTATION} is zero and no on-screen Activity expresses a
         * preference, this rotation value will be used. Must be one of the
         * {@link android.view.Surface#ROTATION_0 Surface rotation constants}.
         *
         */
        //当ACCELEROMETER_ROTATION为0的时候,这个值就是屏幕的方向
        public static final String USER_ROTATION = "user_rotation";

7.4.整理一下

  • 旋转开关关闭 ,freezeRotation ,ACCELEROMETER_ROTATION 为0,USER_ROTATION 为 0或者当前角度
  • 旋转开关打开 ,thawRotation ,ACCELEROMETER_ROTATION 为1 ,USER_ROTATION 为0 这个好像没看到限制旋转180度的逻辑,只看到allow all rotation为false的时候,关闭旋转以后,角度默认变成0了,见7.2

8.DisplayRotation.java

手机模式会限制180度旋转的逻辑,在这个类里

8.1.getAllowAllRotations

  • 和5.2小节用的字段一样 config_allowAllRotations ,决定是否所有角度都可以旋转,
  • 在8.8小节的 88行代码出使用
    private int getAllowAllRotations() {
        if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) {
            mAllowAllRotations = mContext.getResources().getBoolean(
                    R.bool.config_allowAllRotations)
                    ? ALLOW_ALL_ROTATIONS_ENABLED
                    : ALLOW_ALL_ROTATIONS_DISABLED;
        }

        return mAllowAllRotations;
    }

8.2.构造方法

        if (isDefaultDisplay) {
            final Handler uiHandler = UiThread.getHandler();
            //旋转方向监听器
            mOrientationListener = new OrientationListener(mContext, uiHandler);
            mOrientationListener.setCurrentRotation(mRotation);
            //监听设置里的旋转开关的状态改变
            mSettingsObserver = new SettingsObserver(uiHandler);
            //见8.3
            mSettingsObserver.observe();
//..
        }

8.3.OrientationListener

  • 这个方法和我们平时在app里使用的OrientationEventListener方法逻辑差不多,
  • 父类WindowOrientationListener里同样是sensorManager注册listener来监听传感器的数据变化,根据x,y,z的值算出角度,具体逻辑就不看了,
  • enable方法就是开启监听,disable方法就是关闭监听
  • onProposedRotationChanged方法就是父类计算出的角度回调。
    private class OrientationListener extends WindowOrientationListener implements Runnable {
        transient boolean mEnabled;
        //..
        @Override
        public void onProposedRotationChanged(@Surface.Rotation int rotation) {
            mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
            //
            if (isRotationChoiceAllowed(rotation)) {
                final boolean isValid = isValidRotationChoice(rotation);
                sendProposedRotationChangeToStatusBarInternal(rotation, isValid);
            } else {
            //走这里,见8.5
                mService.updateRotation(false /* alwaysSendConfiguration */,
                        false /* forceRelayout */);
            }
        }

        @Override
        public void enable() {
            mEnabled = true;
            getHandler().post(this);//执行run方法
        }

        @Override
        public void disable() {
            mEnabled = false;
            getHandler().post(this);//执行run方法
        }

        @Override
        public void run() {
        //调用父类的方法,开启或者关闭传感器监听
            if (mEnabled) {
                super.enable();
            } else {
                super.disable();
            }
        }
    }

8.4.updateOrientationListenerLw

  • 方法的注解列出了enable以及disable监听器的条件。

    /**
     * Various use cases for invoking this function:
     * <li>Screen turning off, should always disable listeners if already enabled.</li>
     * <li>Screen turned on and current app has sensor based orientation, enable listeners
     *     if not already enabled.</li>
     * <li>Screen turned on and current app does not have sensor orientation, disable listeners
     *     if already enabled.</li>
     * <li>Screen turning on and current app has sensor based orientation, enable listeners
     *     if needed.</li>
     * <li>screen turning on and current app has nosensor based orientation, do nothing.</li>
     */
    private void updateOrientationListenerLw() {
        if (mOrientationListener == null || !mOrientationListener.canDetectOrientation()) {
            // If sensor is turned off or nonexistent for some reason.
            return;
        }
        //屏幕 trun on的时候为true,turn off的时候为false
        final boolean screenOnEarly = mDisplayPolicy.isScreenOnEarly();
        final boolean awake = mDisplayPolicy.isAwake();
        final boolean keyguardDrawComplete = mDisplayPolicy.isKeyguardDrawComplete();
        final boolean windowManagerDrawComplete = mDisplayPolicy.isWindowManagerDrawComplete();


        boolean disable = true;
        //下边就是enable监听器的条件
        //1:屏幕点亮
        //2:唤醒状态或者传感器需要在待机状态下工作
        //3:锁屏界面绘制完成并且窗口绘制完成
        if (screenOnEarly
                && (awake || mOrientationListener.shouldStayEnabledWhileDreaming())
                && ((keyguardDrawComplete && windowManagerDrawComplete))) {
            //这里又判断了是否需要监听器,默认是需要的,具体逻辑自己看    
            if (needSensorRunning()) {
                disable = false;
                //监听器没打开的话打开监听器
                if (!mOrientationListener.mEnabled) {
                    mOrientationListener.enable();
                }
            }
        }
        //关闭监听器,比如息屏
        if (disable) {
            mOrientationListener.disable();
        }
    }
  • 这个方法的调用地方在本类里有3处

>更新角度的时候

    boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
        if (newOrientation == mLastOrientation && !forceUpdate) {
            return false;
        }
        mLastOrientation = newOrientation;
        if (newOrientation != mCurrentAppOrientation) {
            mCurrentAppOrientation = newOrientation;
            if (isDefaultDisplay) {
                updateOrientationListenerLw();
            }
        }
        return updateRotationUnchecked(forceUpdate);
    }

>settings发生变化的时候

    private boolean updateSettings() {
    //..
            if (mShowRotationSuggestions != showRotationSuggestions) {
                shouldUpdateOrientationListener = true;
            }
            //..
            if (mUserRotationMode != userRotationMode) {
                shouldUpdateOrientationListener = true;
                shouldUpdateRotation = true;
            }
            if (shouldUpdateOrientationListener) {
                updateOrientationListenerLw(); // Enable or disable the orientation listener.
            }

>phoneWindowManager

在systemReady方法里,awake方法里, screen turn on/off方法里都会调用到

    public void updateOrientationListener() {
        synchronized (mLock) {
            updateOrientationListenerLw();
        }
    }

8.5.WindowManagerService.java

>updateRotation

8.3小节的监听器回调里会执行这个方法

    public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
        updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
    }
    
    private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
        try {
            synchronized (mGlobalLock) {
                boolean layoutNeeded = false;
                //一般设备就一个屏幕,折叠屏手机不知道算2个不?
                final int displayCount = mRoot.mChildren.size();
                for (int i = 0; i < displayCount; ++i) {
                    final DisplayContent displayContent = mRoot.mChildren.get(i);
                    //根据各种条件的判断,看下最终旋转角度是否发生变化,见 8.6
                    final boolean rotationChanged = displayContent.updateRotationUnchecked();

                    if (rotationChanged) {
                        mAtmService.getTaskChangeNotificationController()
                                .notifyOnActivityRotation(displayContent.mDisplayId);
                    }

                //...

                if (layoutNeeded) {
                    mWindowPlacerLocked.performSurfacePlacement();
                }
            }
        }
    }    

8.6.updateRotationUnchecked

DisplayContent.java

    boolean updateRotationUnchecked() {
        return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */);
    }

8.7.updateRotationUnchecked

这个方法里会计算新的旋转角度,如果角度发生变化的话进行处理

    boolean updateRotationUnchecked(boolean forceUpdate) {
        final int displayId = mDisplayContent.getDisplayId();
    //...
        final int oldRotation = mRotation;
        final int lastOrientation = mLastOrientation;
        //最终计算出的角度是这个,具体看下  8.8
        int rotation = rotationForOrientation(lastOrientation, oldRotation);
        if (mFoldController != null && mFoldController.shouldRevertOverriddenRotation()) {
            int prevRotation = rotation;
            rotation = mFoldController.revertOverriddenRotation();
    //...
        if (oldRotation == rotation) {
            // No change.
            return false;
        } 
 //..
         mRotation = rotation;

        mDisplayContent.setLayoutNeeded();
//..
//后边就是对旋转的处理,不研究了

        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;
    }

8.8.rotationForOrientation

    int rotationForOrientation(@ScreenOrientation int orientation,
            @Surface.Rotation int lastRotation) {
        
        if (isFixedToUserRotation()) {
        //不支持旋转,返回当前的角度
            return mUserRotation;
        }
        //获取listener里通过传感器计算出的角度,或者如果没有传感器,返回-1
        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) {
            //非默认屏幕,忽略传感器数据,使用默认值
            preferredRotation = mUserRotation;
        } else if (lidState == LID_OPEN && mLidOpenRotation >= 0) {
        //输入法旋转开关打开,并且旋转角度大于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)) {
            //手机插在汽车基座上,如果允许传感器的话,使用传感器角度,
            //否则如果汽车基座固定角度大于等于0,使用此角度
            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.
            //演示旋转锁定为true的情况,忽略旋转角度,使用配置的角度
            preferredRotation = mDemoRotation;
        } else if (mDisplayPolicy.isPersistentVrModeEnabled()) {
            //VR模式,固定为竖屏,使用配置的角度
            preferredRotation = mPortraitRotation;
        } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
            //方向锁定的情况,使用当前的角度
            preferredRotation = lastRotation;
        } else if (!mSupportAutoRotation) {
            //不支持自动旋转
            preferredRotation = -1;
        } else if (
        //旋转开关打开
        ((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE
                            || isTabletopAutoRotateOverrideEnabled())
            //并且屏幕方向满足以下5种里的一种
                        && (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) {
            //这里对180度进行了处理 
            if (sensorRotation != Surface.ROTATION_180
                //如果是180度,还必须满足下边3条件之一,旋转角度才能是180度
                //1: allow all rotation 为true,配置里设置的,手机默认为false,平板为true
                    || getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED
                    // 2: 旋转方向为full sensor或者full user
                    //调用activity的setRequestedOrientation方法可以设置
                    || 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) {
            //旋转开关关闭的情况,并且屏幕旋转方向不是上边5种里的任意一种,使用用户默认的角度
            preferredRotation = mUserRotation;
        } else {
        
            preferredRotation = -1;
        }
        //下边继续根据屏幕方向对角度进行处理
        switch (orientation) {
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                //要求是竖屏,判断下角度是否是0或者180
                if (isAnyPortrait(preferredRotation)) {
                //旋转角度是竖屏的情况
                    return preferredRotation;
                }
                //旋转角度不是竖屏的,返回默认的竖屏角度0
                return mPortraitRotation;

            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
                //逻辑同竖屏一样
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                //返回0度
                return mLandscapeRotation;

            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
                // Return reverse portrait unless overridden.
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
                //旋转角度不是竖屏的,返回默认的竖屏角度180
                return mUpsideDownRotation;

            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
                // Return seascape unless overridden.
                if (isLandscapeOrSeascape(preferredRotation)) {
                    return preferredRotation;
                }
                //返回270度
                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;
        }
    }

>ps

通过上边代码里88行的if条件可以知道,手机模式下,app其实是可以旋转到180度的,不过必须设置activity的屏幕旋转为下边2种里边的一种

                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
                    || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER

也就是需要在activity里调用如下的代码

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
//setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);

9.总结

  • 手机桌面app,屏幕旋转的控制逻辑,简单学习下桌面设置页面
  • 学习下快速设置里旋转开关打开关闭以后会修改哪些值,见小节 5
  • 小节8就是限制手机旋转到180度的关键代码,具体逻辑在8.8
全部评论

相关推荐

躺尸修仙中:因为很多92的也去卷中小厂,反正投递简历不要钱,面试不要钱,时间冲突就推,不冲突就面试积累经验
点赞 评论 收藏
分享
评论
1
收藏
分享
牛客网
牛客企业服务