加上動態申請權限
This commit is contained in:
parent
3b25ab2ab7
commit
e5ba0fe34b
@ -3,8 +3,11 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<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
|
<application
|
||||||
|
android:name=".MyApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
@ -39,6 +42,18 @@
|
|||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/i_o_s_clock_widget_info" />
|
android:resource="@xml/i_o_s_clock_widget_info" />
|
||||||
</receiver>
|
</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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -34,8 +34,6 @@ class AlarmService : Service() {
|
|||||||
|
|
||||||
isServiceRunning = true
|
isServiceRunning = true
|
||||||
|
|
||||||
//RetrofitClient.init("http://192.168.0.173:8080")
|
|
||||||
|
|
||||||
startForeground()
|
startForeground()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +103,7 @@ class AlarmService : Service() {
|
|||||||
companion object {
|
companion object {
|
||||||
private val TAG = AlarmService::class.java.simpleName
|
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"
|
private const val NOTIFICATION_SERVICE_CH = "clock.service"
|
||||||
|
|
||||||
|
|||||||
@ -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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,52 @@
|
|||||||
package com.ray650128.iosclockwidget
|
package com.ray650128.iosclockwidget
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
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(
|
startForegroundService(
|
||||||
Intent(this@MainActivity, AlarmService::class.java)
|
Intent(this@MainActivity, AlarmService::class.java)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
169
app/src/main/java/com/ray650128/iosclockwidget/PermissionUtil.kt
Normal file
169
app/src/main/java/com/ray650128/iosclockwidget/PermissionUtil.kt
Normal 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>?)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user