Android通知欄增加快捷開關的技術實現
我們通常可以在通知欄上看到“飛航模式”、“移動資料”、“螢幕錄製”等開關按鈕,這些按鈕都屬於通知欄上的快捷開關,點選快捷開關可以輕易呼叫某種系統能力或開啟某個應用程式的特定頁面。那是否可以在通知欄上自定義一個快捷開關呢?答案是可以的,具體是通過TileService的方案實現。
TileService繼承自Service,所以它也是Android的四大元件之一,不過它是一個特殊的元件,開發者不需要手動開啟呼叫,系統可以自動識別並完成呼叫,系統會通過繫結服務(bindService)的方式呼叫。
建立使用:
快捷開關是Android 7(target 24)的新能力,因此在使用該能力前必須先判斷版本大小(大於等於target 24)。
1、自定義一個TileService類。
```
class MyQSTileService: TileService() {
override fun onTileAdded() {
super.onTileAdded()
}
override fun onStartListening() {
super.onStartListening()
}
override fun onStopListening() {
super.onStopListening()
}
override fun onClick() {
super.onClick()
}
override fun onTileRemoved() {
super.onTileRemoved()
}
}
```
TileService是通過繫結服務(bindService)的方式被呼叫的,因此,繫結服務生命週期包含的四種典型的回撥方法(onCreate()、onBind()、onUnbind()和 onDestroy())都會被呼叫。但是,TileService也包含了以下特殊的生命週期回撥方法:
- onTileAdded():當用戶從編輯欄新增快捷開關到通知欄的快速設定中會呼叫。
- onTileRemoved():當用戶從通知欄的快速設定移除快捷開關時呼叫。
- onClick():當用戶點選快捷開關時呼叫。
- onStartListening():當用戶開啟通知欄的快速設定時呼叫。當快捷開關並沒有從編輯欄拖到設定欄中不會呼叫。在TileAdded新增之後會呼叫一次。
- onStopListening():當用戶開啟通知欄的快速設定時呼叫。當快捷開關並沒有從編輯欄拖到設定欄中不會呼叫。在TileRemoved移除之前會呼叫一次。
2、在應用程式的清單檔案中宣告TileService
。
<service
android:name=".MyQSTileService"
android:label="@string/my_default_tile_label"
android:icon="@drawable/my_default_icon_label"
android:exported="true"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
- name:自定義的TileService
的類名。
- label:快捷開關在通知欄上顯示的名稱。
- icon:快捷開關在通知欄上顯示的圖示。
- exported:該服務能否被外部應用呼叫。該屬性必須為true。如果為false,那麼快捷開關的功能將失效,原因是exported="false"時,TileService
將不支援外部應用調起,手機系統自然不能再和該快捷開關互動。必須配置。
- permission:需要給service配置的許可權,BIND_QUICK_SETTINGS_TILE即允許應用程式繫結到第三方快速設定。必須配置。
- intent-filter:意圖過濾器,只有匹配內部的action,才能調起該service。必須配置。
監聽模式
TileService的監聽模式(或理解為啟動模式)有兩種,一種是主動模式,另一種是標準模式。
- 主動模式
在主動模式下,TileService被請求時該服務會被繫結,並且TileService的onStartListening也會被呼叫。該模式需要在AndroidManifeast清單檔案中宣告:
<service ...>
<meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
android:value="true" />
...
</service>
通過TileService.requestListeningState()這一靜態方法,就可以實現對TileService的請求,示例如下:
TileService.requestListeningState(
applicationContext, ComponentName(
BuildConfig.APPLICATION_ID,
MyQSTileService::class.java.name
)
)
主動模式下值得注意的是:
- 使用者在通知欄快速設定的地方點選快捷開關時,TileService會自動完成繫結、TileService的onStartListening會被呼叫。
- TileService無論是通過點選被繫結還是通過requestListeningState請求被繫結,TileService所在的程序都會被調起。
標準模式
在標準模式下,TileService可見時(即使用者下拉通知欄看見快捷開關)該服務會被繫結,並且TileService的onStartListening也會被呼叫。標準模式不需要在AndroidManifeast清單檔案中進行額外的宣告,預設就是標準模式。
標準模式下值得注意的是:
- 和主動模式相同,TileService被繫結時,TileService所在的程序就會被調起。
- 而和主動模式不同的是,標準模式繫結TileService是通過使用者下拉通知欄實現的,這意味著TileService所在的程序會被多次調起。因此為了避免主程序被頻繁調起、避免DAU等資料統計受到影響,我們還需要為TileService指定一個特定的子程序,在Androidmanifest清單檔案中設定:
<service
......
android:process="自定義子程序的名稱">
......
</service>
更新快捷開關
如果需要對快捷開關的資料進行更新,可以通過getQsTile()獲取快捷開關的物件,然後通過setIcon(更新icon)、setLable(更新名稱)、setState(更新狀態,包括STATE_ACTIVE——表示開啟或啟用狀態、STATE_INACTIVE——表示關閉或暫停狀態、STATE_UNAVAILABLE:表示暫時不可用狀態,在此狀態下,使用者無法與您的磁貼互動)等方法設定快捷開關新的資料,最後呼叫updateTile()方法實現。
override fun onStartListening() {
super.onStartListening()
if (qsTile.state === Tile.STATE_ACTIVE) {
qsTile.label = "inactive"
qsTile.icon = Icon.createWithResource(context, R.drawable.inactive)
qsTile.state = Tile.STATE_INACTIVE
} else {
qsTile.label = "active"
qsTile.icon = Icon.createWithResource(context, R.drawable.active)
qsTile.state = Tile.STATE_ACTIVE
}
qsTile.updateTile()
}
操作快捷開關
- 如果想要實現點選快捷開關時、關閉通知欄並跳轉到某個頁面,可以呼叫以下方法:
startActivityAndCollapse(Intent intent)
- 如果想要在點選快捷開關時彈出對話方塊進行互動,可以呼叫以下方法:
override fun onClick() {
super.onClick()
if(!isLocked()) {
showDialog()
}
}
因為快捷開關有可能在使用者鎖屏時出現,所以必須加上isLocked()的判斷。只有非鎖屏的情況下,對話方塊才會出現。
- 如果快捷開關含有敏感資訊,需要使用isSecure()進行裝置安全性判斷,當裝置安全時,才能執行快捷開關相關的邏輯(如點選的邏輯)。當裝置不安全時(手機處於鎖屏狀態時),可呼叫unlockAndRun(Runnable runnable),提示使用者解鎖螢幕並執行自定義的runnable操作。
以上是通知欄增加快捷開關的全部介紹。歡迎關注公眾號度熊君,一起分享交流。