Android 懸浮窗及權(quán)限

引言:需要實(shí)現(xiàn)一個(gè)視頻懸浮播放的功能,功能實(shí)現(xiàn)后發(fā)現(xiàn)懸浮權(quán)限的檢測(cè)與申請(qǐng)并沒(méi)有想象中那樣簡(jiǎn)單恒削。

時(shí)間:2017年04月10日23:44:46

作者:JustDo23

01. 前言

看到懸浮窗最先想到的就是360懸浮小球和視頻播放的懸浮小窗,懸浮功能通過(guò)WindowManager來(lái)實(shí)現(xiàn)驱入,另外懸浮功能需要使用到相關(guān)的懸浮窗(SYSTEM_ALERT_WINDOW)權(quán)限俐载,在 Android 6.0 之前 Google 并沒(méi)有對(duì)這個(gè)權(quán)限進(jìn)行單獨(dú)處理,國(guó)內(nèi)各個(gè)手機(jī)廠商訂制 ROOM 進(jìn)行后授權(quán)界面各不相同缓淹,懸浮權(quán)限的檢測(cè)與申請(qǐng)的適配問(wèn)題還需注意一下哈打。

02. 懸浮窗口

利用WindowManager來(lái)實(shí)現(xiàn)懸浮小球等需求

  1. android懸浮窗口的實(shí)現(xiàn)
  2. Android桌面懸浮窗效果實(shí)現(xiàn),仿360手機(jī)衛(wèi)士懸浮窗效果
  3. Android桌面懸浮窗進(jìn)階讯壶,QQ手機(jī)管家小火箭效果實(shí)現(xiàn)
  4. 像360懸浮窗那樣料仗,用WindowManager實(shí)現(xiàn)炫酷的懸浮迷你音樂(lè)盒(上)
  5. 像360懸浮窗那樣,用WindowManager實(shí)現(xiàn)炫酷的懸浮迷你音樂(lè)盒(下)

03. 懸浮權(quán)限

實(shí)現(xiàn)懸浮需要在功能清單中添加權(quán)限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

這在 Android 中是一個(gè)特殊權(quán)限伏蚊,Android 中的權(quán)限有一般權(quán)限立轧、危險(xiǎn)權(quán)限和兩個(gè)特殊權(quán)限。

04. 權(quán)限適配

Android 懸浮窗權(quán)限各機(jī)型各系統(tǒng)適配大全

https://github.com/zhaozepeng/FloatWindowPermission

https://github.com/czy1121/settingscompat

看過(guò)大量文章之后找到這篇對(duì)懸浮權(quán)限的檢測(cè)及跳轉(zhuǎn)授權(quán)界面適配比較全面的文章,看過(guò)文章和源碼之后簡(jiǎn)單的繪制了一張表格

api<19 api>=19 && api<23 api>=23
檢測(cè) 默認(rèn)擁有 小米華為魅族360需要檢測(cè)其他默認(rèn)擁有 通用的檢測(cè)
請(qǐng)求 不用跳轉(zhuǎn) 小米華為魅族360各自跳轉(zhuǎn)其他不用跳轉(zhuǎn) 通用的跳轉(zhuǎn)

05. 權(quán)限檢測(cè)

權(quán)限檢測(cè)的總體方法

/**
 * 懸浮窗權(quán)限判斷
 *
 * @param context 上下文
 * @return [ true, 有權(quán)限 ][ false, 無(wú)權(quán)限 ]
 */
private boolean checkPermission(Context context) {
  Boolean hasPermission = false;
  if (Build.VERSION.SDK_INT < 19) {
    hasPermission = true;
  } else if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 23) {
    if (RomUtils.checkIsMiuiRom() || RomUtils.checkIsMeizuRom() || RomUtils.checkIsHuaweiRom() || RomUtils.checkIs360Rom()) {// 特殊機(jī)型
      hasPermission = opPermissionCheck(context, 24);
    } else {// 其他機(jī)型
      hasPermission = true;
    }
  } else if (Build.VERSION.SDK_INT >= 23) {// 6.0 版本之后由于 google 增加了對(duì)懸浮窗權(quán)限的管理氛改,所以方式就統(tǒng)一了
    hasPermission = highVersionPermissionCheck(context);
  }
  return hasPermission;
}

中間版本的特殊機(jī)型權(quán)限檢測(cè)

/**
 * [19-23]之間版本通過(guò)[AppOpsManager]的權(quán)限判斷
 *
 * @param context 上下文
 * @param op
 * @return [ true, 有權(quán)限 ][ false, 無(wú)權(quán)限 ]
 */
private boolean opPermissionCheck(Context context, int op) {
  try {
    AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    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) {
    LogUtil.e(Log.getStackTraceString(e));
  }
  return false;
}

高版本的通用權(quán)限檢測(cè)

/**
 * Android 6.0 版本及之后的權(quán)限判斷
 *
 * @param context 上下文
 * @return [ true, 有權(quán)限 ][ false, 無(wú)權(quán)限 ]
 */
private boolean highVersionPermissionCheck(Context context) {
  if (RomUtils.checkIsMeizuRom()) {// 魅族6.0的系統(tǒng)單獨(dú)適配
    return opPermissionCheck(context, 24);
  }
  try {
    Class clazz = Settings.class;
    Method canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context.class);
    return (Boolean) canDrawOverlays.invoke(null, context);
  } catch (Exception e) {
    LogUtil.e(Log.getStackTraceString(e));
  }
  return false;
}

源碼中有提到魅族手機(jī)Android 6.0版本并不能使用通用的檢測(cè)方式帐萎,需要使用低版本的方式。

06. 權(quán)限請(qǐng)求

總體的請(qǐng)求權(quán)限方法的偽代碼

private void requestPermission() {
  if (Build.VERSION.SDK_INT < 19) {
    // 不用跳轉(zhuǎn)
  } else if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 23) {
    if (RomUtils.checkIsMiuiRom() || RomUtils.checkIsMeizuRom() || RomUtils.checkIsHuaweiRom() || RomUtils.checkIs360Rom()) {
      // 分機(jī)型跳轉(zhuǎn)
    } else {
      // 不用跳轉(zhuǎn)
    }
  } else if (Build.VERSION.SDK_INT >= 23) {
    // 通用跳轉(zhuǎn)
  }
}

總體的請(qǐng)求權(quán)限的方法

/**
 * 請(qǐng)求懸浮窗權(quán)限
 *
 * @param context 上下文
 */
private void requestPermission(Context context) {
  if (Build.VERSION.SDK_INT >= 19 && 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);
    }
  } else if (Build.VERSION.SDK_INT >= 23) {
    highVersionPermissionRequest(context);
  }
}

注意:中間版本權(quán)限的檢測(cè)都是一樣的胜卤,都是使用AppOpsManager類的方式并且參數(shù)op的值都是24疆导,這里重點(diǎn)適配的是不同的手機(jī)跳轉(zhuǎn)到不同的授權(quán)界面,而且小米手機(jī)各個(gè)版本的授權(quán)界面并不完全相同瑰艘。

高版本的跳轉(zhuǎn)通用授權(quán)界面

/**
 * Android 6.0 版本及之后的跳轉(zhuǎn)權(quán)限申請(qǐng)界面
 *
 * @param context 上下文
 */
private void highVersionJump2PermissionActivity(Context context) {
  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.e(TAG, Log.getStackTraceString(e));
  }
}

這里同樣需要注意魅族手機(jī)Android 6.0版本仍舊使用中間版本的方式是鬼。

07. 應(yīng)用詳情

在使用即刻APP的時(shí)候仔細(xì)觀察了一下,發(fā)現(xiàn)即刻的視頻懸浮同樣需要使用權(quán)限紫新,在沒(méi)有授權(quán)限的時(shí)候會(huì)彈框提示用戶去應(yīng)用詳情界面去設(shè)置權(quán)限均蜜,提示路徑設(shè)置 > 應(yīng)用 > 即刻,同時(shí)點(diǎn)擊按鈕直接跳轉(zhuǎn)應(yīng)用詳情界面芒率。這種方式雖然簡(jiǎn)單但其實(shí)并不是所有的機(jī)型都可以達(dá)到完美適配的情況囤耳,仍有部分機(jī)型在此界面是無(wú)法跳轉(zhuǎn)到相關(guān)的手機(jī)位置,用戶可能會(huì)一臉茫然偶芍。

/**
 * 跳轉(zhuǎn)應(yīng)用詳情界面
 *
 * @param context
 */
private void jump2DetailActivity(Context context) {
  Intent intent = new Intent();
  intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
  intent.setData(Uri.fromParts("package", context.getPackageName(), null));
  context.startActivity(intent);
}

這樣便可以嘗試簡(jiǎn)單的授權(quán)請(qǐng)求

/**
 * 請(qǐng)求懸浮窗權(quán)限
 *
 * @param context 上下文
 */
private void requestPermission(Context context) {
  if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 23) {
    jump2DetailActivity(context);
  } else if (Build.VERSION.SDK_INT >= 23) {
    highVersionPermissionRequest(context);
  }
}

08. 權(quán)限規(guī)避

  1. Android無(wú)需權(quán)限顯示懸浮窗, 兼談逆向分析app
  2. 突破小米懸浮窗權(quán)限控制--不需要權(quán)限的懸浮窗

其實(shí)這些文章中提到的規(guī)避方案都是在type類型的指定上:

  • WindowManager.LayoutParams.TYPE_TOAST
  • WindowManager.LayoutParams.TYPE_PHONE

09. 第三方庫(kù)

強(qiáng)調(diào)一下找到的比較好的適配庫(kù)

10. 有趣的事

16年國(guó)慶買個(gè)了魅藍(lán) 3s手機(jī)5.x的系統(tǒng)充择,用了一段時(shí)間后在某次寫(xiě)Demo中發(fā)現(xiàn)Toast吐不出來(lái),不明原因匪蟀,然而并沒(méi)有在意椎麦。年底在自如上租了房子,電子門鎖需要從手機(jī)上獲取動(dòng)態(tài)密碼材彪,照樣是Toast吐不出來(lái)观挎,尷尬了好幾天,結(jié)果在一次倒騰中發(fā)現(xiàn)了手機(jī)管家中的權(quán)限管理段化,通知管理-懸浮窗嘁捷。好些國(guó)產(chǎn)手機(jī)都有手機(jī)管家之類的系統(tǒng)軟件,有相關(guān)權(quán)限管理显熏,魅藍(lán) 3s手機(jī)5.x的系統(tǒng)中的手機(jī)管理 > 權(quán)限管理 > 通知管理 > 懸浮窗就可以管理一個(gè)應(yīng)用的懸浮窗權(quán)限雄嚣,就在昨天我升級(jí)手機(jī)系統(tǒng)到Flyme 6.0.2.0A并且是Android 5.1結(jié)果相同路徑的懸浮窗就變成了懸浮通知并能管理懸浮窗權(quán)限,另外應(yīng)用詳情中的懸浮窗變成了桌面懸浮窗真正管理懸浮窗權(quán)限喘蟆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末缓升,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蕴轨,更是在濱河造成了極大的恐慌港谊,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尺棋,死亡現(xiàn)場(chǎng)離奇詭異封锉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)世分,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門阿宅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)困食,“玉大人,你說(shuō)我怎么就攤上這事奴艾。” “怎么了内斯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵蕴潦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我俘闯,道長(zhǎng)潭苞,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任真朗,我火速辦了婚禮此疹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘遮婶。我一直安慰自己蝗碎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布旗扑。 她就那樣靜靜地躺著蹦骑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪臀防。 梳的紋絲不亂的頭發(fā)上眠菇,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音清钥,去河邊找鬼琼锋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛祟昭,可吹牛的內(nèi)容都是我干的缕坎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼篡悟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谜叹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起搬葬,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤荷腊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后急凰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體女仰,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疾忍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乔外。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖一罩,靈堂內(nèi)的尸體忽然破棺而出杨幼,到底是詐尸還是另有隱情,我是刑警寧澤聂渊,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布差购,位于F島的核電站,受9級(jí)特大地震影響汉嗽,放射性物質(zhì)發(fā)生泄漏欲逃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一饼暑、第九天 我趴在偏房一處隱蔽的房頂上張望暖夭。 院中可真熱鬧,春花似錦撵孤、人聲如沸迈着。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)裕菠。三九已至,卻和暖如春闭专,著一層夾襖步出監(jiān)牢的瞬間奴潘,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工影钉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留画髓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓平委,卻偏偏與公主長(zhǎng)得像奈虾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子廉赔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容