加上動態申請權限

This commit is contained in:
Raymond Yang 2023-04-10 16:11:10 +08:00
parent 3b25ab2ab7
commit e5ba0fe34b
7 changed files with 281 additions and 4 deletions

View File

@ -3,8 +3,11 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@ -39,6 +42,18 @@
android:name="android.appwidget.provider"
android:resource="@xml/i_o_s_clock_widget_info" />
</receiver>
<receiver
android:name=".AutoStartReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -34,8 +34,6 @@ class AlarmService : Service() {
isServiceRunning = true
//RetrofitClient.init("http://192.168.0.173:8080")
startForeground()
}
@ -105,7 +103,7 @@ class AlarmService : Service() {
companion object {
private val TAG = AlarmService::class.java.simpleName
const val SERVICE_ID = 0x1
const val SERVICE_ID = 0x101
private const val NOTIFICATION_SERVICE_CH = "clock.service"

View File

@ -0,0 +1,18 @@
package com.ray650128.iosclockwidget
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class AutoStartReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("AutoStartReceiver", "intent: ${intent?.action}")
if (context == null || intent == null) return
if (!PreferenceUtil.isGranted) return
val i = Intent(context, AlarmService::class.java)
context.startForegroundService(i)
Log.d("AutoStartReceiver", "Boot completed.")
}
}

View File

@ -1,15 +1,52 @@
package com.ray650128.iosclockwidget
import android.Manifest
import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
private val callback = object : PermissionUtil.PermissionResultCallback {
override fun onGrant() {
PreferenceUtil.isGranted = true
if (AlarmService.isServiceRunning) return
startForegroundService(
Intent(this@MainActivity, AlarmService::class.java)
)
}
override fun onDeny(denies: ArrayList<String>?) {
PreferenceUtil.isGranted = false
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!AlarmService.isServiceRunning) {
checkPermissionForNotification()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
PermissionUtil.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
}
private fun checkPermissionForNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PermissionUtil.checkPermission(this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
callback
)
} else {
PreferenceUtil.isGranted = true
if (AlarmService.isServiceRunning) return
startForegroundService(
Intent(this@MainActivity, AlarmService::class.java)
)

View File

@ -0,0 +1,20 @@
package com.ray650128.iosclockwidget
import android.app.Application
import android.content.Context
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
var instance: Application? = null
fun getAppContext(): Context {
return instance!!.applicationContext
}
}
}

View File

@ -0,0 +1,169 @@
package com.ray650128.iosclockwidget
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.annotation.NonNull
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
/**
* 權限工具
* 用法
* 1. Activity 中定義所需的權限陣列
* 2.呼叫 PermissionUtil.checkPermissions()
* 3. PermissionResultCallback 中處理權限授與或拒絕的處裡
*/
object PermissionUtil {
/**
* 當權限拒絕時是否顯示系統的應用程式設定頁面
*/
var showSystemSetting: Boolean = true
private const val mRequestCode: Int = 100
private var mPermissionResult: PermissionResultCallback? = null
private var mPermissionDialog: AlertDialog? = null
/**
* 檢查權限
* @param context Activity 對象
* @param permissions 要求的權限陣列
* @param permissionResultCallback 權限授與/拒絕的回呼
*/
fun checkPermission(
context: Activity,
permissions: Array<String>,
@NonNull permissionResultCallback: PermissionResultCallback
) {
mPermissionResult = permissionResultCallback
if (Build.VERSION.SDK_INT < 23) { // 如果系統低於 Android 6.0,不使用動態申請
permissionResultCallback.onGrant()
return
}
// 遍歷整個 permissions 陣列,檢查有哪些權限未允許
val mPermissionList = ArrayList<String>()
for (permission in permissions) {
if (ContextCompat.checkSelfPermission(
context,
permission
) != PackageManager.PERMISSION_GRANTED
) {
// 將沒有允許的權限加入 mPermissionList
mPermissionList.add(permission)
}
}
// 申請權限
if (mPermissionList.size > 0) { // mPermissionList 中有權限沒有被允許
ActivityCompat.requestPermissions(context, permissions, mRequestCode)
} else {
// 全部的權限都已被允許
permissionResultCallback.onGrant()
return
}
}
/**
* onRequestPermissionsResult
* 請務必在 Activity onRequestPermissionsResult 呼叫此方法
* @param context Activity 對象
* @param requestCode
* @param permissions
* @param grantResults
*/
fun onRequestPermissionsResult(
context: Activity,
requestCode: Int,
@NonNull permissions: Array<out String>,
@NonNull grantResults: IntArray
) {
// 檢查是否有權限被拒
var hasPermissionDenied = false
// 被拒絕的權限
val denyList = ArrayList<String>()
if (mRequestCode == requestCode) {
for (i in grantResults.indices) {
if (grantResults[i] == -1) {
hasPermissionDenied = true
denyList.add(permissions[i])
}
}
}
// 如果有權限被拒絕時
if (hasPermissionDenied) {
if (showSystemSetting) {
showSystemPermissionSettingDialog(context, denyList)
} else {
mPermissionResult?.onDeny(denyList)
}
} else {
// 全部的權限都已被允許
mPermissionResult?.onGrant()
}
}
/**
* 顯示系統設定對話框
* @param context
* @param denies 被拒絕的權限
*/
private fun showSystemPermissionSettingDialog(context: Activity, denies: ArrayList<String>?) {
val mPackageName = context.packageName
if (mPermissionDialog == null) {
mPermissionDialog = AlertDialog.Builder(context)
.setMessage("已禁用權限,請手動授與")
.setPositiveButton("前往設定") { _, _ ->
cancelPermissionDialog()
val packageUri = Uri.parse("package:$mPackageName")
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageUri)
context.startActivity(intent)
context.finish()
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
// 關閉對話框
cancelPermissionDialog()
mPermissionResult?.onDeny(denies)
}
.create()
}
mPermissionDialog?.show()
}
/**
* 關閉對話框
*/
private fun cancelPermissionDialog() {
if (mPermissionDialog != null) {
mPermissionDialog?.cancel()
mPermissionDialog = null
}
}
/**
* 動態申請權限的狀態回呼
*/
interface PermissionResultCallback {
/**
* 已授與
*/
fun onGrant()
/**
* 已拒絕
* @param denies 被拒絕的權限
*/
fun onDeny(denies: ArrayList<String>?)
}
}

View File

@ -0,0 +1,20 @@
package com.ray650128.iosclockwidget
import android.content.Context
import java.lang.reflect.Type
/**
* Shared Preferences 工具類別
* @author Raymond Yang
*/
object PreferenceUtil {
private const val MAIN_KEY = "iOS_Clock_Widget"
private const val IS_GRANTED = "IS_GRANTED"
private val sharedPreferences = MyApplication.getAppContext().getSharedPreferences(MAIN_KEY, Context.MODE_PRIVATE)
var isGranted: Boolean
get() = sharedPreferences.getBoolean(IS_GRANTED, false)
set(value) = sharedPreferences.edit().putBoolean(IS_GRANTED, value).apply()
}