通知権限の要求を1回だけにする。権限がない場合はサイドメニューに表示を追加する。

This commit is contained in:
tateisu 2023-02-06 03:08:48 +09:00
parent 90f657ef94
commit 8daad253b9
8 changed files with 85 additions and 14 deletions

View File

@ -156,7 +156,7 @@ class ActMain : AppCompatActivity(),
lateinit var handler: Handler lateinit var handler: Handler
lateinit var appState: AppState lateinit var appState: AppState
private lateinit var sideMenuAdapter: SideMenuAdapter lateinit var sideMenuAdapter: SideMenuAdapter
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// 読み取り専用のプロパティ // 読み取り専用のプロパティ
@ -326,7 +326,9 @@ class ActMain : AppCompatActivity(),
} }
val prNotification = permissionSpecNotification.requester { val prNotification = permissionSpecNotification.requester {
// 特に何もしない launchAndShowError {
afterNotificationGranted()
}
} }
private var startAfterJob: WeakReference<Job>? = null private var startAfterJob: WeakReference<Job>? = null

View File

@ -22,7 +22,6 @@ import jp.juggler.subwaytooter.dialog.actionsDialog
import jp.juggler.subwaytooter.itemviewholder.ItemViewHolder import jp.juggler.subwaytooter.itemviewholder.ItemViewHolder
import jp.juggler.subwaytooter.pref.* import jp.juggler.subwaytooter.pref.*
import jp.juggler.subwaytooter.push.PushWorker import jp.juggler.subwaytooter.push.PushWorker
import jp.juggler.subwaytooter.push.pushRepo
import jp.juggler.subwaytooter.span.MyClickableSpan import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.util.checkPrivacyPolicy import jp.juggler.subwaytooter.util.checkPrivacyPolicy
import jp.juggler.subwaytooter.util.openCustomTab import jp.juggler.subwaytooter.util.openCustomTab
@ -247,13 +246,22 @@ fun ActMain.launchDialogs() {
// テーマ告知 // テーマ告知
themeDefaultChangedDialog() themeDefaultChangedDialog()
// 通知権限の確認 // 通知権限の確認を一度だけ行う
if(!prNotification.checkOrLaunch()) return@launchAndShowError if (!prefDevice.supressRequestNotificationPermission) {
prefDevice.supressRequestNotificationPermission = true
if (!prNotification.checkOrLaunch()) return@launchAndShowError
}
afterNotificationGranted()
}
}
fun ActMain.afterNotificationGranted() {
sideMenuAdapter.filterListItems()
// Workの掃除 // Workの掃除
WorkManager.getInstance(applicationContext).pruneWork() WorkManager.getInstance(applicationContext).pruneWork()
// 定期的にendpointを再登録したい // 定期的にendpointを再登録したい
PushWorker.enqueueRegisterEndpoint(applicationContext, keepAliveMode = true) PushWorker.enqueueRegisterEndpoint(applicationContext, keepAliveMode = true)
}
} }

View File

@ -214,7 +214,8 @@ class SideMenuAdapter(
IT_GROUP_HEADER(1), IT_GROUP_HEADER(1),
IT_DIVIDER(2), IT_DIVIDER(2),
IT_VERSION(3), IT_VERSION(3),
IT_TIMEZONE(4) IT_TIMEZONE(4),
IT_NOTIFICATION_PERMISSION(5),
} }
private class Item( private class Item(
@ -229,6 +230,7 @@ class SideMenuAdapter(
title == 0 -> ItemType.IT_DIVIDER title == 0 -> ItemType.IT_DIVIDER
title == 1 -> ItemType.IT_VERSION title == 1 -> ItemType.IT_VERSION
title == 2 -> ItemType.IT_TIMEZONE title == 2 -> ItemType.IT_TIMEZONE
title == 3 -> ItemType.IT_NOTIFICATION_PERMISSION
icon == 0 -> ItemType.IT_GROUP_HEADER icon == 0 -> ItemType.IT_GROUP_HEADER
else -> ItemType.IT_NORMAL else -> ItemType.IT_NORMAL
} }
@ -239,11 +241,11 @@ class SideMenuAdapter(
else no icon => section header with title else no icon => section header with title
else => menu item with icon and title else => menu item with icon and title
*/ */
private val originalList = listOf(
private val list = arrayOf(
Item(icon = R.drawable.ic_info_outline, title = 1), Item(icon = R.drawable.ic_info_outline, title = 1),
Item(icon = R.drawable.ic_info_outline, title = 2), Item(icon = R.drawable.ic_info_outline, title = 2),
Item(icon = R.drawable.ic_info_outline, title = 3),
Item(), Item(),
Item(title = R.string.account), Item(title = R.string.account),
@ -453,6 +455,8 @@ class SideMenuAdapter(
} }
) )
private var list = originalList
private val iconColor = actMain.attrColor(R.attr.colorTimeSmall) private val iconColor = actMain.attrColor(R.attr.colorTimeSmall)
override fun getCount(): Int = list.size override fun getCount(): Int = list.size
@ -517,6 +521,24 @@ class SideMenuAdapter(
background = null background = null
text = getTimeZoneString(context) text = getTimeZoneString(context)
} }
ItemType.IT_NOTIFICATION_PERMISSION ->
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
isAllCaps = false
text = actMain.getString(R.string.notification_permission_not_granted)
val drawable = createColoredDrawable(actMain, icon, iconColor, 1f)
setCompoundDrawablesRelativeWithIntrinsicBounds(
drawable,
null,
null,
null
)
setOnClickListener {
drawer.closeDrawer(GravityCompat.START)
if (actMain.prNotification.checkOrLaunch()) {
filterListItems()
}
}
}
} }
} }
@ -557,8 +579,20 @@ class SideMenuAdapter(
this.notifyDataSetChanged() this.notifyDataSetChanged()
} }
fun filterListItems() {
list = originalList.filter {
when (it.itemType) {
ItemType.IT_NOTIFICATION_PERMISSION ->
actMain.prNotification.spec.listNotGranded(actMain).isNotEmpty()
else -> true
}
}
notifyDataSetChanged()
}
init { init {
actMain.applicationContext.checkVersion() actMain.applicationContext.checkVersion()
filterListItems()
ListView(actMain).apply { ListView(actMain).apply {
adapter = this@SideMenuAdapter adapter = this@SideMenuAdapter

View File

@ -26,6 +26,7 @@ class PrefDevice(context: Context) {
private const val PREF_UP_ENDPOINT_EXPIRED = "upEndpointExpired" private const val PREF_UP_ENDPOINT_EXPIRED = "upEndpointExpired"
private const val PREF_PUSH_DISTRIBUTOR = "pushDistributor" private const val PREF_PUSH_DISTRIBUTOR = "pushDistributor"
private const val PREF_TIME_LAST_ENDPOINT_REGISTER = "timeLastEndpointRegister" private const val PREF_TIME_LAST_ENDPOINT_REGISTER = "timeLastEndpointRegister"
private const val PREF_SUPRESS_REQUEST_NOTIFICATION_PERMISSION = "supressRequestNotificationPermission"
const val PUSH_DISTRIBUTOR_FCM = "fcm" const val PUSH_DISTRIBUTOR_FCM = "fcm"
const val PUSH_DISTRIBUTOR_NONE = "none" const val PUSH_DISTRIBUTOR_NONE = "none"
@ -71,6 +72,9 @@ class PrefDevice(context: Context) {
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun int(key: String) = if (sp.contains(key)) sp.getInt(key, 0) else null private fun int(key: String) = if (sp.contains(key)) sp.getInt(key, 0) else null
@Suppress("SameParameterValue")
private fun boolean(key: String) = if (sp.contains(key)) sp.getBoolean(key, false) else null
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun String?.saveTo(key: String) = private fun String?.saveTo(key: String) =
edit { it.putString(key, this) } edit { it.putString(key, this) }
@ -82,6 +86,9 @@ class PrefDevice(context: Context) {
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun Int?.saveTo(key: String) = private fun Int?.saveTo(key: String) =
edit { it.putIntNullable(key, this) } edit { it.putIntNullable(key, this) }
@Suppress("SameParameterValue")
private fun Boolean?.saveTo(key: String) =
edit { it.putBooleanNullable(key, this) }
// 認証開始時の状態を覚えておく // 認証開始時の状態を覚えておく
val authServerType: String? get() = string(PREF_AUTH_SERVER_TYPE) val authServerType: String? get() = string(PREF_AUTH_SERVER_TYPE)
@ -138,6 +145,12 @@ class PrefDevice(context: Context) {
value.saveTo(PREF_TIME_LAST_ENDPOINT_REGISTER) value.saveTo(PREF_TIME_LAST_ENDPOINT_REGISTER)
} }
var supressRequestNotificationPermission: Boolean
get() = boolean(PREF_SUPRESS_REQUEST_NOTIFICATION_PERMISSION)?:false
set(value) {
value.saveTo(PREF_SUPRESS_REQUEST_NOTIFICATION_PERMISSION)
}
////////////////////////////////// //////////////////////////////////
// 以下は古い // 以下は古い

View File

@ -27,7 +27,7 @@ class PermissionRequester(
/** /**
* 権限の詳細 * 権限の詳細
*/ */
private val spec: PermissionSpec, val spec: PermissionSpec,
/** /**
* 権限が与えられた際に処理を再開するラムダ * 権限が与えられた際に処理を再開するラムダ
* - ラムダの引数にこのPermissionRequester自身が渡される * - ラムダの引数にこのPermissionRequester自身が渡される

View File

@ -1,10 +1,12 @@
package jp.juggler.subwaytooter.util package jp.juggler.subwaytooter.util
import android.Manifest import android.Manifest
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import jp.juggler.subwaytooter.R import jp.juggler.subwaytooter.R
@ -29,6 +31,16 @@ class PermissionSpec(
ContextCompat.checkSelfPermission(context, it) != ContextCompat.checkSelfPermission(context, it) !=
PackageManager.PERMISSION_GRANTED PackageManager.PERMISSION_GRANTED
} }
/**
* - 権限のどれかが不足している
* - 不足した権限のどれかが shouldShowRequestPermissionRationale == trueである
*/
fun shouldShowRational(activity: Activity) =
permissions.any {
ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.shouldShowRequestPermissionRationale(activity, it)
}
} }
val permissionSpecNotification = if (Build.VERSION.SDK_INT >= 33) { val permissionSpecNotification = if (Build.VERSION.SDK_INT >= 33) {

View File

@ -1232,4 +1232,5 @@
<string name="push_message_save_to_download_folder_with_secret_key">Downloadフォルダに保存 (秘密鍵つき。データ安全に注意。これは危険です!)</string> <string name="push_message_save_to_download_folder_with_secret_key">Downloadフォルダに保存 (秘密鍵つき。データ安全に注意。これは危険です!)</string>
<string name="saved_to">保存しました。 %1$s</string> <string name="saved_to">保存しました。 %1$s</string>
<string name="view">見る</string> <string name="view">見る</string>
<string name="notification_permission_not_granted">通知表示権限がありません</string>
</resources> </resources>

View File

@ -1246,4 +1246,5 @@
<string name="alert_test">test alert notification</string> <string name="alert_test">test alert notification</string>
<string name="saved_to">Saved to %1$s</string> <string name="saved_to">Saved to %1$s</string>
<string name="view">View</string> <string name="view">View</string>
<string name="notification_permission_not_granted">notification permission not granted</string>
</resources> </resources>