Android强制设置屏幕旋转方向 Force rotation
第一种方法:
首先检查有没有权限,没有就去申请。申请时会触发frameworks/base/services/core/java/com/android/server/wm/AlertWindowNotification.java里面
弹出可以覆盖view的权限窗口。
检查和处理的code如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName())); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); return; } }
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { if (Settings.canDrawOverlays(this)) { //Already has permission } } }
实际去锁定旋转和恢复的code如下:
public final static int STATE_DEFAULT = 0;
public final static int STATE_PORTRAIT = 1;
public final static int STATE_LANDSCAPE = 8;
WindowManager mWindowManager; View mView; WindowManager.LayoutParams lp; mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); int iFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, iFlags, PixelFormat.TRANSLUCENT ); mView = new View(this);
switch (rotation) { //Normal Operation case MainActivity.STATE_DEFAULT: if (mAdded == true) mWindowManager.removeView(mView); mAdded = false; break; //Force Rotation case MainActivity.STATE_LANDSCAPE: case MainActivity.STATE_PORTRAIT: lp.screenOrientation = rotation; if (mAdded == false) { mWindowManager.addView(mView, lp); mAdded = true; }else{ mWindowManager.updateViewLayout(mView, lp); } break; }
上面的方法在添加system权限后,可以直接获得权限,不再需要申请。
android:sharedUserId="android.uid.system"
第二种方法是仿照SystemUI里面检查旋转方向的方式,类似旋转屏幕后,把auto-rotation disable。
参照frameworks/base/core/java/com/android/internal/view/RotationPolicy.java里面
/** * Returns true if rotation lock is enabled. */ public static boolean isRotationLocked(Context context) { return Settings.System.getIntForUser(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0; } /** * Enables or disables rotation lock from the system UI toggle. */ public static void setRotationLock(Context context, final boolean enabled) { Settings.System.putIntForUser(context.getContentResolver(), Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0, UserHandle.USER_CURRENT); final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION; setRotationLock(enabled, rotation); } 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(); } } catch (RemoteException exc) { Log.w(TAG, "Unable to save auto-rotate setting"); } } }); }
因为直接用RotationPolicy中public的 setRotationLock(Context, final boolean)只能锁定当前已经旋转的屏幕,所以不如直接仿照这个private的
setRotationLock(final boolean, final int)去呼叫
import android.view.IWindowManager; import android.view.Surface; import android.view.WindowManagerGlobal; if(value == LANDSCAPE) { AsyncTask.execute(new Runnable() { @Override public void run() { try { IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); //if (enabled) { wm.freezeRotation(Surface.ROTATION_90); /* } else { wm.thawRotation(); }*/ } catch (RemoteException exc) { Log.w(TAG, "Unable to save auto-rotate setting"); } } }); }else { AsyncTask.execute(new Runnable() { @Override public void run() { try { IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); wm.thawRotation(); } catch (RemoteException exc) { Log.w(TAG, "Unable to save auto-rotate setting"); } } }); }
这个需要在源码里面编译,不然只能用反射。
第三种方法:
1. 修改frameworks/base/core/res/res/values/config.xml中
<bool name="config_deskDockEnablesAccelerometer">false</bool> <integer name="config_deskDockRotation">270</integer>
其中第一个参数是在有Dock event时候,不使用Accelerometer自动旋转
第二个参数是在收到Dock event时候, 旋转屏幕的角度。-1为不旋转,可以设置为0-360度
2. 然后参考https://www.cnblogs.com/kunkka/p/10805388.html里面frameworks的修改
在侦测到有device连接时,去发送DOCK event
连接时:
final Intent statusIntent = new Intent(Intent.ACTION_DOCK_EVENT); statusIntent.putExtra(Intent.EXTRA_DOCK_STATE,Intent.EXTRA_DOCK_STATE_DESK); mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
断开连接时:
final Intent statusIntent = new Intent(Intent.ACTION_DOCK_EVENT); statusIntent.putExtra(Intent.EXTRA_DOCK_STATE,Intent.EXTRA_DOCK_STATE_UNDOCKED); mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
此时,在PhoneWindowManager.java中会去读第一步设定的连接DOCK的config,并listen第二步发送的DOCK event去旋转屏幕。
PhoneWindowManager里面是android已经做好的DOCK的功能,不需要修改。