效果如下:
實現方案:
1、ChangeOrientationService.java
/** * @描述 強制旋轉屏幕服務 * @作者 tll * @時間 2018/1/5 */ public class ChangeOrientationService extends Service { private final IBinder mBinder = new LocalBinder(); @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { WindowManager mWindow = (WindowManager) getSystemService(WINDOW_SERVICE); CustomLayout mLayout = new CustomLayout(intent.getIntExtra("orientation", 0)); mWindow.addView(new View(this), mLayout); stopSelf(); return START_NOT_STICKY; } public class LocalBinder extends Binder { public LocalBinder() { super(); } } public class CustomLayout extends android.view.WindowManager.LayoutParams { public CustomLayout(int paramInt) { // TYPE_SYSTEOM_OVERLAY:the layer that is on the top of the window(位於窗口頂部的層) super(0, 0, TYPE_SYSTEM_OVERLAY, FLAG_FULLSCREEN | FLAG_NOT_FOCUSABLE, PixelFormat.RGBX_8888); // push object to the top of its container, not changing its size.(將對象推到容器的頂部,而不是改變其大小。) this.gravity = Gravity.TOP; // set the screenOrientation as desired(按需要設置屏幕方向) this.screenOrientation = paramInt; } } }
2.AndroidManifest.xml
<service android:name=".services.ChangeOrientationService" android:permission="android.permission.WRITE_SETTINGS"/>
3.fragment里面使用
/** * 請求用戶給予懸浮窗的權限 */ public boolean askForOverlayPermission() { if (!MyWindowManager.checkPermission()) {//檢測懸浮窗權限 MyWindowManager.applyPermission(getActivity());//跳轉到懸浮窗權限界面 return false; } else {//開啟橫屏服務 Intent mIntent = new Intent(getActivity(), ChangeOrientationService.class); mIntent.putExtra("orientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); getActivity().startService(mIntent); return true; } }
4.懸浮窗相關的類
public class MyWindowManager { private static final String TAG = "MyWindowManager"; /** * 小懸浮窗View的參數 */ private WindowManager.LayoutParams smallWindowParams; /** * 用於控制在屏幕上添加或移除懸浮窗 */ private WindowManager mWindowManager; private SensorManager sm; private Sensor sensor; /** * 如果WindowManager還未創建,則創建一個新的WindowManager返回。否則返回當前已創建的WindowManager。 * * @param context 必須為應用程序的Context. * @return WindowManager的實例,用於控制在屏幕上添加或移除懸浮窗。 */ private WindowManager getWindowManager(Context context) { if (mWindowManager == null) { mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } return mWindowManager; } public static boolean checkPermission() { //6.0 版本之后由於 google 增加了對懸浮窗權限的管理,所以方式就統一了 if (Build.VERSION.SDK_INT < 23) { if (RomUtils.checkIsMiuiRom()) { return miuiPermissionCheck(BaseApplication.getInstance()); } else if (RomUtils.checkIsMeizuRom()) { return meizuPermissionCheck(BaseApplication.getInstance()); } else if (RomUtils.checkIsHuaweiRom()) { return huaweiPermissionCheck(BaseApplication.getInstance()); } else if (RomUtils.checkIs360Rom()) { return qikuPermissionCheck(BaseApplication.getInstance()); } } return commonROMPermissionCheck(BaseApplication.getInstance()); } private static boolean huaweiPermissionCheck(Context context) { return HuaweiUtils.checkFloatWindowPermission(context); } private static boolean miuiPermissionCheck(Context context) { return MiuiUtils.checkFloatWindowPermission(context); } private static boolean meizuPermissionCheck(Context context) { return MeizuUtils.checkFloatWindowPermission(context); } private static boolean qikuPermissionCheck(Context context) { return QikuUtils.checkFloatWindowPermission(context); } private static boolean commonROMPermissionCheck(Context context) { //最新發現魅族6.0的系統這種方式不好用,天殺的,只有你是奇葩,沒辦法,單獨適配一下 if (RomUtils.checkIsMeizuRom()) { return meizuPermissionCheck(context); } else { Boolean result = true; if (Build.VERSION.SDK_INT >= 23) { try { Class clazz = Settings.class; Method canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context.class); result = (Boolean) canDrawOverlays.invoke(null, context); } catch (Exception e) { Log.e(TAG, "commonROMPermissionCheck: " + Log.getStackTraceString(e)); } } return result; } } public static void applyPermission(Context context) { if (Build.VERSION.SDK_INT < 23) { if (RomUtils.checkIsMiuiRom()) { miuiROMPermissionApply(context); } else if (RomUtils.checkIsMeizuRom()) { meizuROMPermissionApply(context); } else if (RomUtils.checkIsHuaweiRom()) { huaweiROMPermissionApply(context); } else if (RomUtils.checkIs360Rom()) { ROM360PermissionApply(context); } } commonROMPermissionApply(context); } public interface OnConfirmResult { void confirmResult(boolean confirm); } private static void showConfirmDialog(Context context, OnConfirmResult result) { showConfirmDialog(context, "您的手機沒有授予懸浮窗權限,請開啟后再試", result); } private static void showConfirmDialog(Context context, String message, final OnConfirmResult result) { android.support.v7.app.AlertDialog dialog = new android.support.v7.app.AlertDialog.Builder(context).setCancelable(true).setTitle("") .setMessage(message) .setPositiveButton("現在去開啟", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirmResult(true); dialog.dismiss(); } }).setNegativeButton("暫不開啟", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirmResult(false); dialog.dismiss(); } }).create(); dialog.show(); /*if (dlg != null && dlg.isShowing()) { dlg.dismiss(); } dlg = new UpdateDlg(context, false); dlg.show(); dlg.setContent("", message); dlg.setButton("暫不開啟", "現在去開啟"); dlg.setListener(new View.OnClickListener() { @Override public void onClick(View v) { result.confirmResult(true); dlg.dismiss(); } });*/ } private static void ROM360PermissionApply(final Context context) { showConfirmDialog(context, new OnConfirmResult() { @Override public void confirmResult(boolean confirm) { if (confirm) { QikuUtils.applyPermission(context); } else { Log.d(TAG, "ROM:360, user manually refuse OVERLAY_PERMISSION "); } } }); } private static void huaweiROMPermissionApply(final Context context) { showConfirmDialog(context, new OnConfirmResult() { @Override public void confirmResult(boolean confirm) { if (confirm) { HuaweiUtils.applyPermission(context); } else { Log.d(TAG, "ROM:huawei, user manually refuse OVERLAY_PERMISSION"); } } }); } private static void meizuROMPermissionApply(final Context context) { showConfirmDialog(context, new OnConfirmResult() { @Override public void confirmResult(boolean confirm) { if (confirm) { MeizuUtils.applyPermission(context); } else { Log.d(TAG, "ROM:meizu, user manually refuse OVERLAY_PERMISSION"); } } }); } private static void miuiROMPermissionApply(final Context context) { showConfirmDialog(context, new OnConfirmResult() { @Override public void confirmResult(boolean confirm) { if (confirm) { MiuiUtils.applyMiuiPermission(context); } else { Log.d(TAG, "ROM:miui, user manually refuse OVERLAY_PERMISSION: "); } } }); } /** * 通用 rom 權限申請 */ private static void commonROMPermissionApply(final Context context) { //這里也一樣,魅族系統需要單獨適配 if (RomUtils.checkIsMeizuRom()) { meizuROMPermissionApply(context); } else { if (Build.VERSION.SDK_INT >= 23) { showConfirmDialog(context, new OnConfirmResult() { @Override public void confirmResult(boolean confirm) { if (confirm) { try { Class clazz = Settings.class; Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION"); Intent intent = new Intent(field.get(null).toString()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.parse("package:" + context.getPackageName())); context.startActivity(intent); } catch (Exception e) { Log.d(TAG, "confirmResult: " + Log.getStackTraceString(e)); } } else { Log.d(TAG, "user manually refuse OVERLAY_PERMISSION"); //需要做統計效果 } } }); } } } }
public class HuaweiUtils { private static final String TAG = "HuaweiUtils"; /** * 檢測 Huawei 懸浮窗權限 */ public static boolean checkFloatWindowPermission(Context context) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; } return true; } /** * 去華為權限申請頁面 */ public static void applyPermission(Context context) { try { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//華為權限管理 // ComponentName comp = new ComponentName("com.huawei.systemmanager", // "com.huawei.permissionmanager.ui.SingleAppActivity");//華為權限管理,跳轉到指定app的權限管理位置需要華為接口權限,未解決 ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//懸浮窗管理頁面 intent.setComponent(comp); if (RomUtils.getEmuiVersion() == 3.1) { //emui 3.1 的適配 context.startActivity(intent); } else { //emui 3.0 的適配 comp = new ComponentName("com.huawei.systemmanager", "com.huawei.notificationmanager.ui.NotificationManagmentActivity");//懸浮窗管理頁面 intent.setComponent(comp); context.startActivity(intent); } } catch (SecurityException e) { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//華為權限管理 ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");//華為權限管理,跳轉到本app的權限管理頁面,這個需要華為接口權限,未解決 // ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//懸浮窗管理頁面 intent.setComponent(comp); context.startActivity(intent); Log.e(TAG, Log.getStackTraceString(e)); } catch (ActivityNotFoundException e) { /** * 手機管家版本較低 HUAWEI SC-UL10 */ // Toast.makeText(MainActivity.this, "act找不到", Toast.LENGTH_LONG).show(); Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ComponentName comp = new ComponentName("com.Android.settings", "com.android.settings.permission.TabItem");//權限管理頁面 android4.4 // ComponentName comp = new ComponentName("com.android.settings","com.android.settings.permission.single_app_activity");//此處可跳轉到指定app對應的權限管理頁面,但是需要相關權限,未解決 intent.setComponent(comp); context.startActivity(intent); e.printStackTrace(); Log.e(TAG, Log.getStackTraceString(e)); } catch (Exception e) { //拋出異常時提示信息 Toast.makeText(context, "進入設置頁面失敗,請手動設置", Toast.LENGTH_LONG).show(); Log.e(TAG, Log.getStackTraceString(e)); } } @TargetApi(Build.VERSION_CODES.KITKAT) private static boolean checkOp(Context context, int op) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); try { Class clazz = AppOpsManager.class; Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); } catch (Exception e) { Log.e(TAG, Log.getStackTraceString(e)); } } else { Log.e(TAG, "Below API 19 cannot invoke!"); } return false; } }
public class MeizuUtils { private static final String TAG = "MeizuUtils"; /** * 檢測 meizu 懸浮窗權限 */ public static boolean checkFloatWindowPermission(Context context) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; } return true; } /** * 去魅族權限申請頁面 */ public static void applyPermission(Context context){ Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity"); intent.putExtra("packageName", context.getPackageName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } @TargetApi(Build.VERSION_CODES.KITKAT) private static boolean checkOp(Context context, int op) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); try { Class clazz = AppOpsManager.class; Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); return AppOpsManager.MODE_ALLOWED == (int)method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); } catch (Exception e) { Log.e(TAG, Log.getStackTraceString(e)); } } else { Log.e(TAG, "Below API 19 cannot invoke!"); } return false; } }
public class MiuiUtils { private static final String TAG = "MiuiUtils"; /** * 獲取小米 rom 版本號,獲取失敗返回 -1 * * @return miui rom version code, if fail , return -1 */ public static int getMiuiVersion() { String version = RomUtils.getSystemProperty("ro.miui.ui.version.name"); if (version != null) { try { return Integer.parseInt(version.substring(1)); } catch (Exception e) { Log.e(TAG, "get miui version code error, version : " + version); Log.e(TAG, Log.getStackTraceString(e)); } } return -1; } /** * 檢測 miui 懸浮窗權限 */ public static boolean checkFloatWindowPermission(Context context) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; } else { // if ((context.getApplicationInfo().flags & 1 << 27) == 1) { // return true; // } else { // return false; // } return true; } } @TargetApi(Build.VERSION_CODES.KITKAT) private static boolean checkOp(Context context, int op) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); try { Class clazz = AppOpsManager.class; Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); } catch (Exception e) { Log.e(TAG, Log.getStackTraceString(e)); } } else { Log.e(TAG, "Below API 19 cannot invoke!"); } return false; } /** * 小米 ROM 權限申請 */ public static void applyMiuiPermission(Context context) { int versionCode = getMiuiVersion(); if (versionCode == 5) { goToMiuiPermissionActivity_V5(context); } else if (versionCode == 6) { goToMiuiPermissionActivity_V6(context); } else if (versionCode == 7) { goToMiuiPermissionActivity_V7(context); } else if (versionCode == 8) { goToMiuiPermissionActivity_V8(context); } else { Log.e(TAG, "this is a special MIUI rom version, its version code " + versionCode); } } private static boolean isIntentAvailable(Intent intent, Context context) { if (intent == null) { return false; } return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; } /** * 小米 V5 版本 ROM權限申請 */ public static void goToMiuiPermissionActivity_V5(Context context) { Intent intent = null; String packageName = context.getPackageName(); intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", packageName, null); intent.setData(uri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (isIntentAvailable(intent, context)) { context.startActivity(intent); } else { Log.e(TAG, "intent is not available!"); } //設置頁面在應用詳情頁面 // Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); // PackageInfo pInfo = null; // try { // pInfo = context.getPackageManager().getPackageInfo // (HostInterfaceManager.getHostInterface().getApp().getPackageName(), 0); // } catch (PackageManager.NameNotFoundException e) { // AVLogUtils.e(TAG, e.getMessage()); // } // intent.setClassName("com.android.settings", "com.miui.securitycenter.permission.AppPermissionsEditor"); // intent.putExtra("extra_package_uid", pInfo.applicationInfo.uid); // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // if (isIntentAvailable(intent, context)) { // context.startActivity(intent); // } else { // AVLogUtils.e(TAG, "Intent is not available!"); // } } /** * 小米 V6 版本 ROM權限申請 */ public static void goToMiuiPermissionActivity_V6(Context context) { Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); intent.putExtra("extra_pkgname", context.getPackageName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (isIntentAvailable(intent, context)) { context.startActivity(intent); } else { Log.e(TAG, "Intent is not available!"); } } /** * 小米 V7 版本 ROM權限申請 */ public static void goToMiuiPermissionActivity_V7(Context context) { Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); intent.putExtra("extra_pkgname", context.getPackageName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (isIntentAvailable(intent, context)) { context.startActivity(intent); } else { Log.e(TAG, "Intent is not available!"); } } /** * 小米 V8 版本 ROM權限申請 */ public static void goToMiuiPermissionActivity_V8(Context context) { Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); // intent.setPackage("com.miui.securitycenter"); intent.putExtra("extra_pkgname", context.getPackageName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (isIntentAvailable(intent, context)) { context.startActivity(intent); } else { intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); intent.setPackage("com.miui.securitycenter"); intent.putExtra("extra_pkgname", context.getPackageName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (isIntentAvailable(intent, context)) { context.startActivity(intent); } else { Log.e(TAG, "Intent is not available!"); } } } }
public class QikuUtils { private static final String TAG = "QikuUtils"; /** * 檢測 360 懸浮窗權限 */ public static boolean checkFloatWindowPermission(Context context) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; } return true; } @TargetApi(Build.VERSION_CODES.KITKAT) private static boolean checkOp(Context context, int op) { final int version = Build.VERSION.SDK_INT; if (version >= 19) { AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); try { Class clazz = AppOpsManager.class; Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); return AppOpsManager.MODE_ALLOWED == (int)method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); } catch (Exception e) { Log.e(TAG, Log.getStackTraceString(e)); } } else { Log.e("", "Below API 19 cannot invoke!"); } return false; } /** * 去360權限申請頁面 */ public static void applyPermission(Context context) { Intent intent = new Intent(); intent.setClassName("com.android.settings", "com.android.settings.Settings$OverlaySettingsActivity"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (isIntentAvailable(intent, context)) { context.startActivity(intent); } else { intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.appEnterActivity"); if (isIntentAvailable(intent, context)) { context.startActivity(intent); } else { Log.e(TAG, "can't open permission page with particular name, please use " + "\"adb shell dumpsys activity\" command and tell me the name of the float window permission page"); } } } private static boolean isIntentAvailable(Intent intent, Context context) { if (intent == null) { return false; } return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; } }
public class RomUtils { private static final String TAG = "RomUtils"; /** * 獲取 emui 版本號 * @return */ public static double getEmuiVersion() { try { String emuiVersion = getSystemProperty("ro.build.version.emui"); String version = emuiVersion.substring(emuiVersion.indexOf("_") + 1); return Double.parseDouble(version); } catch (Exception e) { e.printStackTrace(); } return 4.0; } /** * 獲取小米 rom 版本號,獲取失敗返回 -1 * * @return miui rom version code, if fail , return -1 */ public static int getMiuiVersion() { String version = getSystemProperty("ro.miui.ui.version.name"); if (version != null) { try { return Integer.parseInt(version.substring(1)); } catch (Exception e) { Log.e(TAG, "get miui version code error, version : " + version); } } return -1; } public static String getSystemProperty(String propName) { String line; BufferedReader input = null; try { Process p = Runtime.getRuntime().exec("getprop " + propName); input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); line = input.readLine(); input.close(); } catch (IOException ex) { Log.e(TAG, "Unable to read sysprop " + propName, ex); return null; } finally { if (input != null) { try { input.close(); } catch (IOException e) { Log.e(TAG, "Exception while closing InputStream", e); } } } return line; } public static boolean checkIsHuaweiRom() { return Build.MANUFACTURER.contains("HUAWEI"); } /** * check if is miui ROM */ public static boolean checkIsMiuiRom() { return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name")); } public static boolean checkIsMeizuRom() { //return Build.MANUFACTURER.contains("Meizu"); String meizuFlymeOSFlag = getSystemProperty("ro.build.display.id"); if (TextUtils.isEmpty(meizuFlymeOSFlag)){ return false; }else if (meizuFlymeOSFlag.contains("flyme") || meizuFlymeOSFlag.toLowerCase().contains("flyme")){ return true; }else { return false; } } public static boolean checkIs360Rom() { return Build.MANUFACTURER.contains("QiKU"); } }