1+ package com.mubarak.mbcompass.core.permission
2+
3+ import android.Manifest
4+ import android.app.AlertDialog
5+ import android.content.Context
6+ import android.content.Intent
7+ import android.content.pm.PackageManager
8+ import android.net.Uri
9+ import android.os.Build
10+ import android.provider.Settings
11+ import androidx.activity.result.ActivityResultLauncher
12+ import androidx.core.content.ContextCompat
13+ import androidx.fragment.app.Fragment
14+ import com.mubarak.mbcompass.R
15+
16+ class PermissionHandler (
17+ private val fragment : Fragment
18+ ) {
19+
20+ private val context: Context
21+ get() = fragment.requireContext()
22+
23+
24+ fun requestLocationPermission (
25+ launcher : ActivityResultLauncher <String >,
26+ onGranted : () -> Unit ,
27+ onDenied : () -> Unit
28+ ) {
29+ when {
30+ // Already granted
31+ hasLocationPermission() -> {
32+ onGranted()
33+ }
34+
35+ // Should show rationale (user denied before)
36+ fragment.shouldShowRequestPermissionRationale(Manifest .permission.ACCESS_FINE_LOCATION ) -> {
37+ showLocationRationaleDialog(
38+ onPositive = { launcher.launch(Manifest .permission.ACCESS_FINE_LOCATION ) },
39+ onNegative = onDenied
40+ )
41+ }
42+
43+ // First time asking
44+ else -> {
45+ showLocationEducationDialog(
46+ onPositive = { launcher.launch(Manifest .permission.ACCESS_FINE_LOCATION ) },
47+ onNegative = onDenied
48+ )
49+ }
50+ }
51+ }
52+
53+
54+ private fun showLocationEducationDialog (
55+ onPositive : () -> Unit ,
56+ onNegative : () -> Unit
57+ ) {
58+ AlertDialog .Builder (context)
59+ .setTitle(R .string.location_permission_title)
60+ .setMessage(R .string.location_permission_message)
61+ .setIcon(R .drawable.location_icon24px)
62+ .setPositiveButton(R .string.grant_permission) { _, _ ->
63+ onPositive()
64+ }
65+ .setNegativeButton(R .string.not_now) { _, _ ->
66+ onNegative()
67+ }
68+ .setCancelable(false )
69+ .show()
70+ }
71+
72+
73+ // Rationale dialog
74+ private fun showLocationRationaleDialog (
75+ onPositive : () -> Unit ,
76+ onNegative : () -> Unit
77+ ) {
78+ AlertDialog .Builder (context)
79+ .setTitle(R .string.location_permission_required)
80+ .setMessage(R .string.location_permission_rationale)
81+ .setIcon(R .drawable.location_icon24px)
82+ .setPositiveButton(R .string.try_again) { _, _ ->
83+ onPositive()
84+ }
85+ .setNeutralButton(R .string.open_settings) { _, _ ->
86+ openAppSettings()
87+ }
88+ .setNegativeButton(R .string.cancel, null )
89+ .show()
90+ }
91+
92+
93+ fun hasLocationPermission (): Boolean {
94+ return ContextCompat .checkSelfPermission(
95+ context,
96+ Manifest .permission.ACCESS_FINE_LOCATION
97+ ) == PackageManager .PERMISSION_GRANTED
98+ }
99+
100+
101+ fun requestNotificationPermission (
102+ launcher : ActivityResultLauncher <String >,
103+ onGranted : () -> Unit ,
104+ onDenied : () -> Unit
105+ ) {
106+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .TIRAMISU ) {
107+ // Not needed on Android 12 and below
108+ onGranted()
109+ return
110+ }
111+
112+ when {
113+ hasNotificationPermission() -> {
114+ onGranted()
115+ }
116+
117+ fragment.shouldShowRequestPermissionRationale(Manifest .permission.POST_NOTIFICATIONS ) -> {
118+ showNotificationRationaleDialog(
119+ onPositive = { launcher.launch(Manifest .permission.POST_NOTIFICATIONS ) },
120+ onNegative = onDenied
121+ )
122+ }
123+
124+ else -> {
125+ showNotificationEducationDialog(
126+ onPositive = { launcher.launch(Manifest .permission.POST_NOTIFICATIONS ) },
127+ onNegative = onDenied
128+ )
129+ }
130+ }
131+ }
132+
133+ private fun showNotificationEducationDialog (
134+ onPositive : () -> Unit ,
135+ onNegative : () -> Unit
136+ ) {
137+ AlertDialog .Builder (context)
138+ .setTitle(R .string.notification_permission_title)
139+ .setMessage(R .string.notification_permission_message)
140+ .setPositiveButton(R .string.grant_permission) { _, _ ->
141+ onPositive()
142+ }
143+ .setNegativeButton(R .string.not_now) { _, _ ->
144+ onNegative()
145+ }
146+ .setCancelable(false )
147+ .show()
148+ }
149+
150+ private fun showNotificationRationaleDialog (
151+ onPositive : () -> Unit ,
152+ onNegative : () -> Unit
153+ ) {
154+ AlertDialog .Builder (context)
155+ .setTitle(R .string.notification_permission_required)
156+ .setMessage(R .string.notification_permission_rationale)
157+ .setPositiveButton(R .string.try_again) { _, _ ->
158+ onPositive()
159+ }
160+ .setNeutralButton(R .string.open_settings) { _, _ ->
161+ openAppSettings()
162+ }
163+ .setNegativeButton(R .string.cancel, null )
164+ .show()
165+ }
166+
167+ fun hasNotificationPermission (): Boolean {
168+ return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ) {
169+ ContextCompat .checkSelfPermission(
170+ context,
171+ Manifest .permission.POST_NOTIFICATIONS
172+ ) == PackageManager .PERMISSION_GRANTED
173+ } else {
174+ true // Not required on older versions
175+ }
176+ }
177+
178+ fun requestActivityRecognitionPermission (
179+ launcher : ActivityResultLauncher <String >,
180+ onGranted : () -> Unit ,
181+ onDenied : () -> Unit
182+ ) {
183+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .Q ) {
184+ // Not needed on Android 9 and below
185+ onGranted()
186+ return
187+ }
188+
189+ when {
190+ hasActivityRecognitionPermission() -> {
191+ onGranted()
192+ }
193+
194+ fragment.shouldShowRequestPermissionRationale(Manifest .permission.ACTIVITY_RECOGNITION ) -> {
195+ showActivityRecognitionRationaleDialog(
196+ onPositive = { launcher.launch(Manifest .permission.ACTIVITY_RECOGNITION ) },
197+ onNegative = onDenied
198+ )
199+ }
200+
201+ else -> {
202+ showActivityRecognitionEducationDialog(
203+ onPositive = { launcher.launch(Manifest .permission.ACTIVITY_RECOGNITION ) },
204+ onNegative = onDenied
205+ )
206+ }
207+ }
208+ }
209+
210+ private fun showActivityRecognitionEducationDialog (
211+ onPositive : () -> Unit ,
212+ onNegative : () -> Unit
213+ ) {
214+ AlertDialog .Builder (context)
215+ .setTitle(R .string.activity_recognition_permission_title)
216+ .setMessage(R .string.activity_recognition_permission_message)
217+ .setPositiveButton(R .string.grant_permission) { _, _ ->
218+ onPositive()
219+ }
220+ .setNegativeButton(R .string.not_now) { _, _ ->
221+ onNegative()
222+ }
223+ .setCancelable(false )
224+ .show()
225+ }
226+
227+ private fun showActivityRecognitionRationaleDialog (
228+ onPositive : () -> Unit ,
229+ onNegative : () -> Unit
230+ ) {
231+ AlertDialog .Builder (context)
232+ .setTitle(R .string.activity_recognition_permission_required)
233+ .setMessage(R .string.activity_recognition_permission_rationale)
234+ .setPositiveButton(R .string.try_again) { _, _ ->
235+ onPositive()
236+ }
237+ .setNeutralButton(R .string.open_settings) { _, _ ->
238+ openAppSettings()
239+ }
240+ .setNegativeButton(R .string.cancel, null )
241+ .show()
242+ }
243+
244+ fun hasActivityRecognitionPermission (): Boolean {
245+ return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q ) {
246+ ContextCompat .checkSelfPermission(
247+ context,
248+ Manifest .permission.ACTIVITY_RECOGNITION
249+ ) == PackageManager .PERMISSION_GRANTED
250+ } else {
251+ true // Not required on older versions
252+ }
253+ }
254+
255+ fun openAppSettings () {
256+ val intent = Intent (Settings .ACTION_APPLICATION_DETAILS_SETTINGS ).apply {
257+ data = Uri .fromParts(" package" , context.packageName, null )
258+ flags = Intent .FLAG_ACTIVITY_NEW_TASK
259+ }
260+ context.startActivity(intent)
261+ }
262+
263+
264+ fun openLocationSettings () {
265+ val intent = Intent (Settings .ACTION_LOCATION_SOURCE_SETTINGS ).apply {
266+ flags = Intent .FLAG_ACTIVITY_NEW_TASK
267+ }
268+ context.startActivity(intent)
269+ }
270+ }
0 commit comments