diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActPushMessageList.kt b/app/src/main/java/jp/juggler/subwaytooter/ActPushMessageList.kt index 9a3e4013..7d700754 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActPushMessageList.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActPushMessageList.kt @@ -21,8 +21,8 @@ import jp.juggler.subwaytooter.databinding.ActPushMessageListBinding import jp.juggler.subwaytooter.databinding.LvPushMessageBinding import jp.juggler.subwaytooter.dialog.actionsDialog import jp.juggler.subwaytooter.dialog.runInProgress -import jp.juggler.subwaytooter.notification.NotificationIconAndColor -import jp.juggler.subwaytooter.notification.notificationIconAndColor +import jp.juggler.subwaytooter.notification.PushMessageIconColor +import jp.juggler.subwaytooter.notification.iconColor import jp.juggler.subwaytooter.push.pushRepo import jp.juggler.subwaytooter.table.PushMessage import jp.juggler.subwaytooter.table.daoAccountNotificationStatus @@ -165,11 +165,13 @@ class ActPushMessageList : AppCompatActivity() { } private val tintIconMap = HashMap() - fun tintIcon(ic: NotificationIconAndColor) = + + fun tintIcon(ic: PushMessageIconColor) = tintIconMap.getOrPut(ic.name) { - val src = ContextCompat.getDrawable(this@ActPushMessageList, ic.iconId)!! + val context = this + val src = ContextCompat.getDrawable(context, ic.iconId)!! DrawableCompat.wrap(src).also { - DrawableCompat.setTint(it, ic.color) + DrawableCompat.setTint(it, ContextCompat.getColor(context, ic.colorRes)) } } @@ -190,7 +192,7 @@ class ActPushMessageList : AppCompatActivity() { pm ?: return lastItem = pm - val iconAndColor = pm.notificationIconAndColor() + val iconAndColor = pm.iconColor() Glide.with(views.ivSmall) .load(pm.iconSmall) .error(tintIcon(iconAndColor)) diff --git a/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt b/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt index c5d8bb33..7525a2f4 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt @@ -25,7 +25,6 @@ import jp.juggler.subwaytooter.column.startLoading import jp.juggler.subwaytooter.dialog.actionsDialog import jp.juggler.subwaytooter.dialog.pickAccount import jp.juggler.subwaytooter.dialog.runInProgress -import jp.juggler.subwaytooter.notification.PushSubscriptionHelper import jp.juggler.subwaytooter.notification.checkNotificationImmediate import jp.juggler.subwaytooter.notification.checkNotificationImmediateAll import jp.juggler.subwaytooter.notification.recycleClickedNotification @@ -47,7 +46,6 @@ import jp.juggler.util.queryIntentActivitiesCompat import kotlinx.coroutines.CancellationException import kotlinx.coroutines.withContext import org.unifiedpush.android.connector.UnifiedPush -import java.util.ArrayList private val log = LogCategory("ActMainIntent") @@ -351,7 +349,7 @@ fun ActMain.handleSharedIntent(intent: Intent) { } // アカウントを追加/更新したらappServerHashの取得をやりなおす -fun ActMain.updatePushDistributer(){ +fun ActMain.updatePushDistributer() { when { fcmHandler.noFcm && prefDevice.pushDistributor.isNullOrEmpty() -> { try { @@ -361,7 +359,7 @@ fun ActMain.updatePushDistributer(){ // 選択しなかった場合は購読の更新を行わない } } - else -> PushWorker.enqueueRegisterEndpoint(this) + else -> PushWorker.enqueueRegisterEndpoint(this) } } diff --git a/app/src/main/java/jp/juggler/subwaytooter/notification/PushSubscriptionHelper.kt b/app/src/main/java/jp/juggler/subwaytooter/notification/PushSubscriptionHelper.kt index 9a2cfb96..b282a658 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/notification/PushSubscriptionHelper.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/notification/PushSubscriptionHelper.kt @@ -1,488 +1,488 @@ package jp.juggler.subwaytooter.notification - -import android.content.Context -import jp.juggler.subwaytooter.R -import jp.juggler.subwaytooter.api.TootApiClient -import jp.juggler.subwaytooter.api.TootApiResult -import jp.juggler.subwaytooter.api.entity.* -import jp.juggler.subwaytooter.table.SavedAccount -import jp.juggler.subwaytooter.table.SubscriptionServerKey -import jp.juggler.subwaytooter.table.appDatabase -import jp.juggler.util.* -import jp.juggler.util.data.* -import jp.juggler.util.log.* -import jp.juggler.util.network.toPostRequestBuilder -import jp.juggler.util.ui.* -import okhttp3.Request - -class PushSubscriptionHelper( - val context: Context, - val account: SavedAccount, - private val verbose: Boolean = false, - private val daoSubscriptionServerKey: SubscriptionServerKey.Access = - SubscriptionServerKey.Access(appDatabase), -) { - companion object { - private val log = LogCategory("PushSubscriptionHelper") - } - - private val logBuffer = StringBuilder() - - val logString: String - get() = logBuffer.toString() - - private var subscribed: Boolean = false - - val flags = account.notificationFlags() - - private fun addLog(s: String?) { - if (s?.isNotEmpty() == true) { - if (logBuffer.isNotEmpty()) logBuffer.append('\n') - logBuffer.append(s) - } - } - - // アプリサーバにendpoint URLの変更を伝える - private suspend fun registerEndpoint( - client: TootApiClient, - deviceId: String, - endpoint: String, - ): TootApiResult { - - // deprecated - // if (account.last_push_endpoint == endpoint) return TootApiResult() - - return client.http( - buildJsonObject { - put("acct", account.acct.ascii) - put("deviceId", deviceId) - put("endpoint", endpoint) - } - .toPostRequestBuilder() - .url("$APP_SERVER/webpushendpoint") - .build() - ).also { result -> - result.response?.let { res -> - when (res.code.also { res.close() }) { - in 200 until 300 -> { - // deprecated - // account.updateLastPushEndpoint(endpoint) - } - else -> { - result.caption = "(SubwayTooter App server)" - client.readBodyString(result) - } - } - } - } - } - -// suspend fun updateSubscription( -// client: TootApiClient, -// force: Boolean = false, -// progress: suspend (SavedAccount, PollingState) -> Unit = { _, _ -> }, -// ): TootApiResult? = try { -// when { -// account.isPseudo -> -// TootApiResult(context.getString(R.string.pseudo_account_not_supported)) // -// else -> { -// progress(account, PollingState.CheckPushSubscription) -// when { -// account.isMisskey -> updateSubscriptionMisskey(client) -// else -> updateSubscriptionMastodon(client, force) +//import android.content.Context +//import jp.juggler.subwaytooter.R +//import jp.juggler.subwaytooter.api.TootApiClient +//import jp.juggler.subwaytooter.api.TootApiResult +//import jp.juggler.subwaytooter.api.entity.* +//import jp.juggler.subwaytooter.table.SavedAccount +//import jp.juggler.subwaytooter.table.SubscriptionServerKey +//import jp.juggler.subwaytooter.table.appDatabase +//import jp.juggler.util.* +//import jp.juggler.util.data.* +//import jp.juggler.util.log.* +//import jp.juggler.util.network.toPostRequestBuilder +//import jp.juggler.util.ui.* +//import okhttp3.Request +// +//class PushSubscriptionHelper( +// val context: Context, +// val account: SavedAccount, +// private val verbose: Boolean = false, +// private val daoSubscriptionServerKey: SubscriptionServerKey.Access = +// SubscriptionServerKey.Access(appDatabase), +//) { +// companion object { +// private val log = LogCategory("PushSubscriptionHelper") +// } +// +// private val logBuffer = StringBuilder() +// +// val logString: String +// get() = logBuffer.toString() +// +// private var subscribed: Boolean = false +// +// val flags = account.notificationFlags() +// +// private fun addLog(s: String?) { +// if (s?.isNotEmpty() == true) { +// if (logBuffer.isNotEmpty()) logBuffer.append('\n') +// logBuffer.append(s) +// } +// } +// +// // アプリサーバにendpoint URLの変更を伝える +// private suspend fun registerEndpoint( +// client: TootApiClient, +// deviceId: String, +// endpoint: String, +// ): TootApiResult { +// +// // deprecated +// // if (account.last_push_endpoint == endpoint) return TootApiResult() +// +// return client.http( +// buildJsonObject { +// put("acct", account.acct.ascii) +// put("deviceId", deviceId) +// put("endpoint", endpoint) +// } +// .toPostRequestBuilder() +// .url("$APP_SERVER/webpushendpoint") +// .build() +// ).also { result -> +// result.response?.let { res -> +// when (res.code.also { res.close() }) { +// in 200 until 300 -> { +// // deprecated +// // account.updateLastPushEndpoint(endpoint) +// } +// else -> { +// result.caption = "(SubwayTooter App server)" +// client.readBodyString(result) +// } // } // } // } -// } catch (ex: Throwable) { -// TootApiResult(ex.withCaption("error.")) -// }?.apply { +// } // -// if (error != null) addLog("$error $requestInfo".trimEnd()) -// -// // update error text on account table -// val log = logString -// when { -// log.contains(ERROR_PREVENT_FREQUENTLY_CHECK) -> { -// // don't update if check was skipped. -// } -// -// subscribed || log.isEmpty() -> Unit -// // clear error text if succeeded or no error log -//// if (account.last_subscription_error != null) { -//// account.updateSubscriptionError(null) +//// suspend fun updateSubscription( +//// client: TootApiClient, +//// force: Boolean = false, +//// progress: suspend (SavedAccount, PollingState) -> Unit = { _, _ -> }, +//// ): TootApiResult? = try { +//// when { +//// account.isPseudo -> +//// TootApiResult(context.getString(R.string.pseudo_account_not_supported)) +//// +//// else -> { +//// progress(account, PollingState.CheckPushSubscription) +//// when { +//// account.isMisskey -> updateSubscriptionMisskey(client) +//// else -> updateSubscriptionMastodon(client, force) //// } +//// } +//// } +//// } catch (ex: Throwable) { +//// TootApiResult(ex.withCaption("error.")) +//// }?.apply { +//// +//// if (error != null) addLog("$error $requestInfo".trimEnd()) +//// +//// // update error text on account table +//// val log = logString +//// when { +//// log.contains(ERROR_PREVENT_FREQUENTLY_CHECK) -> { +//// // don't update if check was skipped. +//// } +//// +//// subscribed || log.isEmpty() -> Unit +//// // clear error text if succeeded or no error log +////// if (account.last_subscription_error != null) { +////// account.updateSubscriptionError(null) +////// } +//// +//// else -> Unit +//// // record error text +////// account.updateSubscriptionError(log) +//// } +//// } // -// else -> Unit -// // record error text -//// account.updateSubscriptionError(log) -// } -// } - -// private suspend fun updateSubscriptionMisskey(client: TootApiClient): TootApiResult? { +//// private suspend fun updateSubscriptionMisskey(client: TootApiClient): TootApiResult? { +//// +//// // 現在の購読状態を取得できないので、毎回購読の更新を行う +//// // FCMのデバイスIDを取得 +//// val deviceId = try { +//// loadFirebaseMessagingToken(context) +//// } catch (ex: Throwable) { +//// log.e(ex, "loadFirebaseMessagingToken failed.") +//// return when (ex) { +//// is CancellationException -> null +//// else -> TootApiResult(error = context.getString(R.string.missing_fcm_device_id)) +//// } +//// } +//// +//// // アクセストークン +//// val accessToken = account.misskeyApiToken +//// ?: return TootApiResult(error = "missing misskeyApiToken.") +//// +//// // インストールIDを取得 +//// val installId = try { +//// loadInstallId( +//// context, +//// account, +//// deviceId +//// ) { a, s -> log.i("[${a.acct.pretty}]${s.desc}") } +//// } catch (ex: Throwable) { +//// log.e(ex, "loadInstallId failed.") +//// return when (ex) { +//// is CancellationException -> null +//// else -> TootApiResult(error = context.getString(R.string.missing_install_id)) +//// } +//// } +//// +//// // クライアント識別子 +//// val clientIdentifier = "$accessToken$installId".digestSHA256Base64Url() +//// +//// // 購読が不要な場合 +//// // アプリサーバが410を返せるように状態を通知する +//// if (flags == 0) return registerEndpoint(client, deviceId, "none").also { +//// if (it.error == null && verbose) addLog(context.getString(R.string.push_subscription_updated)) +//// } +//// +//// /* +//// https://github.com/syuilo/misskey/blob/master/src/services/create-notification.ts#L46 +//// Misskeyは通知に既読の概念があり、イベント発生後2秒たっても未読の時だけプッシュ通知が発生する。 +//// STでプッシュ通知を試すにはSTの画面を非表示にする必要があるのでWebUIを使って投稿していたが、 +//// WebUIを開いていると通知はすぐ既読になるのでプッシュ通知は発生しない。 +//// プッシュ通知のテスト時はST2台を使い、片方をプッシュ通知の受信チェック、もう片方を投稿などの作業に使うことになる。 +//// */ +//// +//// // https://github.com/syuilo/misskey/issues/2541 +//// // https://github.com/syuilo/misskey/commit/4c6fb60dd25d7e2865fc7c4d97728593ffc3c902 +//// // 2018/9/1 の上記コミット以降、Misskeyでもサーバ公開鍵を得られるようになった +//// +//// val endpoint = +//// "$APP_SERVER/webpushcallback/${deviceId.encodePercent()}/${account.acct.ascii.encodePercent()}/$flags/$clientIdentifier/misskey" +//// +//// // アプリサーバが過去のendpoint urlに410を返せるよう、状態を通知する +//// val r = registerEndpoint(client, deviceId, endpoint.toUri().encodedPath!!) +//// if (r.error != null) return r +//// +//// // 購読 +//// @Suppress("SpellCheckingInspection") +//// return client.request( +//// "/api/sw/register", +//// account.putMisskeyApiToken().apply { +//// put("endpoint", endpoint) +//// put("auth", "iRdmDrOS6eK6xvG1H6KshQ") +//// put( +//// "publickey", +//// "BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE" +//// ) +//// } +//// .toPostRequestBuilder() +//// )?.also { result -> +//// val jsonObject = result.jsonObject +//// if (jsonObject == null) { +//// addLog("API error.") +//// } else { +//// if (verbose) addLog(context.getString(R.string.push_subscription_updated)) +//// subscribed = true +//// return updateServerKey( +//// client, +//// clientIdentifier, +//// jsonObject.string("key") ?: "3q2+rw" +//// ) +//// } +//// } +//// } // -// // 現在の購読状態を取得できないので、毎回購読の更新を行う -// // FCMのデバイスIDを取得 -// val deviceId = try { -// loadFirebaseMessagingToken(context) -// } catch (ex: Throwable) { -// log.e(ex, "loadFirebaseMessagingToken failed.") -// return when (ex) { -// is CancellationException -> null -// else -> TootApiResult(error = context.getString(R.string.missing_fcm_device_id)) -// } -// } +//// private suspend fun updateSubscriptionMastodon( +//// client: TootApiClient, +//// force: Boolean, +//// ): TootApiResult? { +//// +//// // 現在の購読状態を取得 +//// // https://github.com/tootsuite/mastodon/pull/7471 +//// // https://github.com/tootsuite/mastodon/pull/7472 +//// +//// val subscription404: Boolean +//// val oldSubscription: TootPushSubscription? +//// checkCurrentSubscription(client).let { +//// if (it.failed) return it.result +//// subscription404 = it.is404 +//// oldSubscription = parseItem(::TootPushSubscription, it.result?.jsonObject) +//// } +//// +//// if (oldSubscription == null) { +//// log.i("${account.acct}: oldSubscription is null") +//// val (ti, result) = TootInstance.get(client) +//// ti ?: return result +//// checkInstanceVersionMastodon(ti, subscription404)?.let { return it } +//// } +//// +//// // FCMのデバイスIDを取得 +//// val deviceId = try { +//// loadFirebaseMessagingToken(context) +//// } catch (ex: Throwable) { +//// log.e(ex, "loadFirebaseMessagingToken failed.") +//// return when (ex) { +//// is CancellationException -> null +//// else -> TootApiResult(error = context.getString(R.string.missing_fcm_device_id)) +//// } +//// } +//// +//// // インストールIDを取得 +//// val installId = try { +//// loadInstallId( +//// context, +//// account, +//// deviceId +//// ) { a, s -> log.i("[${a.acct.pretty}]${s.desc}") } +//// } catch (ex: Throwable) { +//// log.e(ex, "loadInstallId failed.") +//// return when (ex) { +//// is CancellationException -> null +//// else -> TootApiResult(error = context.getString(R.string.missing_install_id)) +//// } +//// } +//// // アクセストークン +//// val accessToken = account.bearerAccessToken +//// ?: return TootApiResult(error = "missing access token.") +//// +//// // アクセストークンのダイジェスト +//// val tokenDigest = accessToken.digestSHA256Base64Url() +//// +//// // クライアント識別子 +//// val clientIdentifier = "$accessToken$installId".digestSHA256Base64Url() +//// +//// val endpoint = +//// "$APP_SERVER/webpushcallback/${deviceId.encodePercent()}/${account.acct.ascii.encodePercent()}/$flags/$clientIdentifier" +//// +//// val newAlerts = JsonObject().apply { +//// put("follow", account.notification_follow) +//// put(TootNotification.TYPE_ADMIN_SIGNUP, account.notification_follow) +//// put("favourite", account.notification_favourite) +//// put("reblog", account.notification_boost) +//// put("mention", account.notification_mention) +//// put("poll", account.notification_vote) +//// put("follow_request", account.notification_follow_request) +//// put("status", account.notification_post) +//// put("update", account.notification_update) +//// put("emoji_reaction", account.notification_reaction) // fedibird拡張 +//// } +//// +//// if (!force) { +//// canSkipSubscriptionMastodon( +//// client = client, +//// clientIdentifier = clientIdentifier, +//// endpoint = endpoint, +//// oldSubscription = oldSubscription, +//// newAlerts = newAlerts, +//// )?.let { return it } +//// } +//// +//// // アクセストークンの優先権を取得 +//// checkDeviceHasPriority( +//// client, +//// tokenDigest = tokenDigest, +//// installId = installId, +//// ).let { +//// if (it.failed) return it.result +//// } +//// +//// return when (flags) { +//// // 通知設定が全てカラなので、購読を取り消したい +//// 0 -> unsubscribeMastodon(client) +//// +//// // 通知設定が空ではないので購読を行いたい +//// else -> subscribeMastodon( +//// client = client, +//// clientIdentifier = clientIdentifier, +//// endpoint = endpoint, +//// newAlerts = newAlerts +//// ) +//// } +//// } // -// // アクセストークン -// val accessToken = account.misskeyApiToken -// ?: return TootApiResult(error = "missing misskeyApiToken.") -// -// // インストールIDを取得 -// val installId = try { -// loadInstallId( -// context, -// account, -// deviceId -// ) { a, s -> log.i("[${a.acct.pretty}]${s.desc}") } -// } catch (ex: Throwable) { -// log.e(ex, "loadInstallId failed.") -// return when (ex) { -// is CancellationException -> null -// else -> TootApiResult(error = context.getString(R.string.missing_install_id)) -// } -// } -// -// // クライアント識別子 -// val clientIdentifier = "$accessToken$installId".digestSHA256Base64Url() -// -// // 購読が不要な場合 -// // アプリサーバが410を返せるように状態を通知する -// if (flags == 0) return registerEndpoint(client, deviceId, "none").also { -// if (it.error == null && verbose) addLog(context.getString(R.string.push_subscription_updated)) -// } -// -// /* -// https://github.com/syuilo/misskey/blob/master/src/services/create-notification.ts#L46 -// Misskeyは通知に既読の概念があり、イベント発生後2秒たっても未読の時だけプッシュ通知が発生する。 -// STでプッシュ通知を試すにはSTの画面を非表示にする必要があるのでWebUIを使って投稿していたが、 -// WebUIを開いていると通知はすぐ既読になるのでプッシュ通知は発生しない。 -// プッシュ通知のテスト時はST2台を使い、片方をプッシュ通知の受信チェック、もう片方を投稿などの作業に使うことになる。 -// */ -// -// // https://github.com/syuilo/misskey/issues/2541 -// // https://github.com/syuilo/misskey/commit/4c6fb60dd25d7e2865fc7c4d97728593ffc3c902 -// // 2018/9/1 の上記コミット以降、Misskeyでもサーバ公開鍵を得られるようになった -// -// val endpoint = -// "$APP_SERVER/webpushcallback/${deviceId.encodePercent()}/${account.acct.ascii.encodePercent()}/$flags/$clientIdentifier/misskey" -// -// // アプリサーバが過去のendpoint urlに410を返せるよう、状態を通知する -// val r = registerEndpoint(client, deviceId, endpoint.toUri().encodedPath!!) -// if (r.error != null) return r -// -// // 購読 -// @Suppress("SpellCheckingInspection") -// return client.request( -// "/api/sw/register", -// account.putMisskeyApiToken().apply { -// put("endpoint", endpoint) -// put("auth", "iRdmDrOS6eK6xvG1H6KshQ") -// put( -// "publickey", -// "BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE" -// ) -// } -// .toPostRequestBuilder() -// )?.also { result -> -// val jsonObject = result.jsonObject -// if (jsonObject == null) { -// addLog("API error.") -// } else { -// if (verbose) addLog(context.getString(R.string.push_subscription_updated)) -// subscribed = true -// return updateServerKey( -// client, -// clientIdentifier, -// jsonObject.string("key") ?: "3q2+rw" -// ) -// } -// } -// } - -// private suspend fun updateSubscriptionMastodon( -// client: TootApiClient, -// force: Boolean, +// // returns null if no error +// private fun checkInstanceVersionMastodon( +// ti: TootInstance, +// subscription404: Boolean, // ): TootApiResult? { // -// // 現在の購読状態を取得 -// // https://github.com/tootsuite/mastodon/pull/7471 -// // https://github.com/tootsuite/mastodon/pull/7472 -// -// val subscription404: Boolean -// val oldSubscription: TootPushSubscription? -// checkCurrentSubscription(client).let { -// if (it.failed) return it.result -// subscription404 = it.is404 -// oldSubscription = parseItem(::TootPushSubscription, it.result?.jsonObject) -// } -// -// if (oldSubscription == null) { -// log.i("${account.acct}: oldSubscription is null") -// val (ti, result) = TootInstance.get(client) -// ti ?: return result -// checkInstanceVersionMastodon(ti, subscription404)?.let { return it } -// } -// -// // FCMのデバイスIDを取得 -// val deviceId = try { -// loadFirebaseMessagingToken(context) -// } catch (ex: Throwable) { -// log.e(ex, "loadFirebaseMessagingToken failed.") -// return when (ex) { -// is CancellationException -> null -// else -> TootApiResult(error = context.getString(R.string.missing_fcm_device_id)) -// } -// } -// -// // インストールIDを取得 -// val installId = try { -// loadInstallId( -// context, -// account, -// deviceId -// ) { a, s -> log.i("[${a.acct.pretty}]${s.desc}") } -// } catch (ex: Throwable) { -// log.e(ex, "loadInstallId failed.") -// return when (ex) { -// is CancellationException -> null -// else -> TootApiResult(error = context.getString(R.string.missing_install_id)) -// } -// } -// // アクセストークン -// val accessToken = account.bearerAccessToken -// ?: return TootApiResult(error = "missing access token.") -// -// // アクセストークンのダイジェスト -// val tokenDigest = accessToken.digestSHA256Base64Url() -// -// // クライアント識別子 -// val clientIdentifier = "$accessToken$installId".digestSHA256Base64Url() -// -// val endpoint = -// "$APP_SERVER/webpushcallback/${deviceId.encodePercent()}/${account.acct.ascii.encodePercent()}/$flags/$clientIdentifier" -// -// val newAlerts = JsonObject().apply { -// put("follow", account.notification_follow) -// put(TootNotification.TYPE_ADMIN_SIGNUP, account.notification_follow) -// put("favourite", account.notification_favourite) -// put("reblog", account.notification_boost) -// put("mention", account.notification_mention) -// put("poll", account.notification_vote) -// put("follow_request", account.notification_follow_request) -// put("status", account.notification_post) -// put("update", account.notification_update) -// put("emoji_reaction", account.notification_reaction) // fedibird拡張 -// } -// -// if (!force) { -// canSkipSubscriptionMastodon( -// client = client, -// clientIdentifier = clientIdentifier, -// endpoint = endpoint, -// oldSubscription = oldSubscription, -// newAlerts = newAlerts, -// )?.let { return it } -// } -// -// // アクセストークンの優先権を取得 -// checkDeviceHasPriority( -// client, -// tokenDigest = tokenDigest, -// installId = installId, -// ).let { -// if (it.failed) return it.result -// } -// -// return when (flags) { -// // 通知設定が全てカラなので、購読を取り消したい -// 0 -> unsubscribeMastodon(client) -// -// // 通知設定が空ではないので購読を行いたい -// else -> subscribeMastodon( -// client = client, -// clientIdentifier = clientIdentifier, -// endpoint = endpoint, -// newAlerts = newAlerts +// // 2.4.0rc1 未満にはプッシュ購読APIはない +// if (!ti.versionGE(TootInstance.VERSION_2_4_0_rc1)) { +// return TootApiResult( +// context.getString(R.string.instance_does_not_support_push_api, ti.version) // ) // } +// +// if (subscription404 && flags == 0) { +// when { +// ti.versionGE(TootInstance.VERSION_2_4_0_rc2) -> { +// // 購読が不要で現在の状況が404だった場合 +// // 2.4.0rc2以降では「購読が存在しない」を示すので何もしなくてよい +// if (verbose) addLog(context.getString(R.string.push_subscription_not_exists)) +// return TootApiResult() +// } +// +// else -> { +// // 2.4.0rc1では「APIが存在しない」と「購読が存在しない」を判別できない +// } +// } +// } +// return null // } - - // returns null if no error - private fun checkInstanceVersionMastodon( - ti: TootInstance, - subscription404: Boolean, - ): TootApiResult? { - - // 2.4.0rc1 未満にはプッシュ購読APIはない - if (!ti.versionGE(TootInstance.VERSION_2_4_0_rc1)) { - return TootApiResult( - context.getString(R.string.instance_does_not_support_push_api, ti.version) - ) - } - - if (subscription404 && flags == 0) { - when { - ti.versionGE(TootInstance.VERSION_2_4_0_rc2) -> { - // 購読が不要で現在の状況が404だった場合 - // 2.4.0rc2以降では「購読が存在しない」を示すので何もしなくてよい - if (verbose) addLog(context.getString(R.string.push_subscription_not_exists)) - return TootApiResult() - } - - else -> { - // 2.4.0rc1では「APIが存在しない」と「購読が存在しない」を判別できない - } - } - } - return null - } - - private class CheckCurrentSubscriptionResult( - val result: TootApiResult?, - val failed: Boolean, - val is404: Boolean, - ) - - @Suppress("BooleanLiteralArgument") - private suspend fun checkCurrentSubscription(client: TootApiClient): CheckCurrentSubscriptionResult { - val r = client.request("/api/v1/push/subscription") - fun rvError() = CheckCurrentSubscriptionResult(r, true, false) - fun rvOk() = CheckCurrentSubscriptionResult(r, false, false) - fun rv404() = CheckCurrentSubscriptionResult(r, false, true) - val res = r?.response ?: return rvError() // cancelled or missing response - - if (res.code != 200) log.i("${account.acct}: check existing subscription: code=${res.code}") - - return when (res.code) { - 200 -> { - if (r.error?.isNotEmpty() == true && r.jsonObject == null) { - // Pleromaが200応答でもエラーHTMLを返す場合がある - addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma)) - rvError() - } else { - // たぶん購読が存在する - rvOk() - } - } - - // この時点では存在しないのが購読なのかAPIなのか分からない - 404 -> rv404() - - 403 -> { - // アクセストークンにpushスコープがない - if (flags != 0 || verbose) addLog(context.getString(R.string.missing_push_scope)) - rvError() - } - - in 400 until 500 -> { - addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma)) - rvError() - } - - else -> { - addLog("${res.request}") - addLog("${res.code} ${res.message}") - rvOk() // 後でリトライする - } - } - } - - private suspend fun unsubscribeMastodon( - client: TootApiClient, - ): TootApiResult? { - - val r = client.request("/api/v1/push/subscription", Request.Builder().delete()) - val res = r?.response ?: return r - - return when (res.code) { - 200 -> { - if (verbose) addLog(context.getString(R.string.push_subscription_deleted)) - TootApiResult() - } - - 404 -> { - if (verbose) { - addLog(context.getString(R.string.missing_push_api)) - r - } else { - TootApiResult() - } - } - - 403 -> { - addLog(context.getString(R.string.missing_push_scope)) - r - } - - else -> { - addLog("${res.request}") - addLog("${res.code} ${res.message}") - r - } - } - } - - private suspend fun subscribeMastodon( - client: TootApiClient, - endpoint: String, - newAlerts: JsonObject, - ): TootApiResult? { - @Suppress("SpellCheckingInspection") - val params = JsonObject().apply { - put("subscription", JsonObject().apply { - put("endpoint", endpoint) - put("keys", JsonObject().apply { - put( - "p256dh", - "BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE" - ) - put("auth", "iRdmDrOS6eK6xvG1H6KshQ") - }) - }) - put("data", JsonObject().apply { - put("alerts", newAlerts) - account.pushPolicy?.let { put("policy", it) } - }) - } - - val r = client.request( - "/api/v1/push/subscription", - params.toPostRequestBuilder() - ) ?: return null - - val res = r.response ?: return r - - return when (res.code) { - 404 -> { - addLog(context.getString(R.string.missing_push_api)) - r - } - - 403 -> { - addLog(context.getString(R.string.missing_push_scope)) - r - } - - 200 -> { - subscribed = true - if (verbose) addLog(context.getString(R.string.push_subscription_updated)) - return TootApiResult() - } - - else -> { - addLog(r.jsonObject?.toString()) - r - } - } - } -} +// +// private class CheckCurrentSubscriptionResult( +// val result: TootApiResult?, +// val failed: Boolean, +// val is404: Boolean, +// ) +// +// @Suppress("BooleanLiteralArgument") +// private suspend fun checkCurrentSubscription(client: TootApiClient): CheckCurrentSubscriptionResult { +// val r = client.request("/api/v1/push/subscription") +// fun rvError() = CheckCurrentSubscriptionResult(r, true, false) +// fun rvOk() = CheckCurrentSubscriptionResult(r, false, false) +// fun rv404() = CheckCurrentSubscriptionResult(r, false, true) +// val res = r?.response ?: return rvError() // cancelled or missing response +// +// if (res.code != 200) log.i("${account.acct}: check existing subscription: code=${res.code}") +// +// return when (res.code) { +// 200 -> { +// if (r.error?.isNotEmpty() == true && r.jsonObject == null) { +// // Pleromaが200応答でもエラーHTMLを返す場合がある +// addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma)) +// rvError() +// } else { +// // たぶん購読が存在する +// rvOk() +// } +// } +// +// // この時点では存在しないのが購読なのかAPIなのか分からない +// 404 -> rv404() +// +// 403 -> { +// // アクセストークンにpushスコープがない +// if (flags != 0 || verbose) addLog(context.getString(R.string.missing_push_scope)) +// rvError() +// } +// +// in 400 until 500 -> { +// addLog(context.getString(R.string.instance_does_not_support_push_api_pleroma)) +// rvError() +// } +// +// else -> { +// addLog("${res.request}") +// addLog("${res.code} ${res.message}") +// rvOk() // 後でリトライする +// } +// } +// } +// +// private suspend fun unsubscribeMastodon( +// client: TootApiClient, +// ): TootApiResult? { +// +// val r = client.request("/api/v1/push/subscription", Request.Builder().delete()) +// val res = r?.response ?: return r +// +// return when (res.code) { +// 200 -> { +// if (verbose) addLog(context.getString(R.string.push_subscription_deleted)) +// TootApiResult() +// } +// +// 404 -> { +// if (verbose) { +// addLog(context.getString(R.string.missing_push_api)) +// r +// } else { +// TootApiResult() +// } +// } +// +// 403 -> { +// addLog(context.getString(R.string.missing_push_scope)) +// r +// } +// +// else -> { +// addLog("${res.request}") +// addLog("${res.code} ${res.message}") +// r +// } +// } +// } +// +// private suspend fun subscribeMastodon( +// client: TootApiClient, +// endpoint: String, +// newAlerts: JsonObject, +// ): TootApiResult? { +// @Suppress("SpellCheckingInspection") +// val params = JsonObject().apply { +// put("subscription", JsonObject().apply { +// put("endpoint", endpoint) +// put("keys", JsonObject().apply { +// put( +// "p256dh", +// "BBEUVi7Ehdzzpe_ZvlzzkQnhujNJuBKH1R0xYg7XdAKNFKQG9Gpm0TSGRGSuaU7LUFKX-uz8YW0hAshifDCkPuE" +// ) +// put("auth", "iRdmDrOS6eK6xvG1H6KshQ") +// }) +// }) +// put("data", JsonObject().apply { +// put("alerts", newAlerts) +// account.pushPolicy?.let { put("policy", it) } +// }) +// } +// +// val r = client.request( +// "/api/v1/push/subscription", +// params.toPostRequestBuilder() +// ) ?: return null +// +// val res = r.response ?: return r +// +// return when (res.code) { +// 404 -> { +// addLog(context.getString(R.string.missing_push_api)) +// r +// } +// +// 403 -> { +// addLog(context.getString(R.string.missing_push_scope)) +// r +// } +// +// 200 -> { +// subscribed = true +// if (verbose) addLog(context.getString(R.string.push_subscription_updated)) +// return TootApiResult() +// } +// +// else -> { +// addLog(r.jsonObject?.toString()) +// r +// } +// } +// } +//} diff --git a/app/src/main/java/jp/juggler/subwaytooter/notification/SnsNotificationIcon.kt b/app/src/main/java/jp/juggler/subwaytooter/push/PushMessageIconColor.kt similarity index 61% rename from app/src/main/java/jp/juggler/subwaytooter/notification/SnsNotificationIcon.kt rename to app/src/main/java/jp/juggler/subwaytooter/push/PushMessageIconColor.kt index 7bf4b27d..b3c470d9 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/notification/SnsNotificationIcon.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/push/PushMessageIconColor.kt @@ -1,7 +1,6 @@ -package jp.juggler.subwaytooter.notification +package jp.juggler.subwaytooter.push -import android.graphics.Color -import androidx.annotation.ColorInt +import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import jp.juggler.subwaytooter.R import jp.juggler.subwaytooter.table.PushMessage @@ -9,86 +8,84 @@ import jp.juggler.util.log.LogCategory private val log = LogCategory("NotificationIconAndColor") -enum class NotificationIconAndColor( - @ColorInt colorArg: Int, +enum class PushMessageIconColor( + @ColorRes val colorRes: Int, @DrawableRes val iconId: Int, val keys: Array, ) { Favourite( - 0xe5e825, + R.color.colorNotificationAccentFavourite, R.drawable.ic_star_outline, arrayOf("favourite"), ), Mention( - 0x60f516, + R.color.colorNotificationAccentMention, R.drawable.outline_alternate_email_24, arrayOf("mention"), ), Reply( - 0xff3dbb, + R.color.colorNotificationAccentReply, R.drawable.ic_reply, arrayOf("reply") ), Reblog( - 0x39e3d5, + R.color.colorNotificationAccentReblog, R.drawable.ic_repeat, arrayOf("reblog", "renote"), ), Quote( - 0x40a9ff, + R.color.colorNotificationAccentQuote, R.drawable.ic_quote, arrayOf("quote"), ), Follow( - 0xf57a33, + R.color.colorNotificationAccentFollow, R.drawable.ic_person_add, arrayOf("follow", "followRequestAccepted") ), Unfollow( - 0x9433f5, + R.color.colorNotificationAccentUnfollow, R.drawable.ic_follow_cross, arrayOf("unfollow") ), Reaction( - 0xf5f233, + R.color.colorNotificationAccentReaction, R.drawable.outline_add_reaction_24, arrayOf("reaction", "emoji_reaction", "pleroma:emoji_reaction") ), FollowRequest( - 0xf53333, + R.color.colorNotificationAccentFollowRequest, R.drawable.ic_follow_wait, arrayOf("follow_request", "receiveFollowRequest"), ), Poll( - 0x33f59b, + R.color.colorNotificationAccentPoll, R.drawable.outline_poll_24, arrayOf("pollVote", "poll_vote", "poll"), ), Status( - 0x33f597, + R.color.colorNotificationAccentStatus, R.drawable.ic_edit, arrayOf("status", "update", "status_reference") ), SignUp( - 0xf56a33, + R.color.colorNotificationAccentSignUp, R.drawable.outline_group_add_24, arrayOf("admin.sign_up"), ), Unknown( - 0xae1aed, + R.color.colorNotificationAccentUnknown, R.drawable.ic_question, - arrayOf("unknown"), + arrayOf("unknown", "admin.sign_up"), ) ; - val color = Color.BLACK or colorArg - companion object { val map = buildMap { values().forEach { for (k in it.keys) { - val old: NotificationIconAndColor? = get(k) + val old: PushMessageIconColor? = get(k) if (old != null) { error("NotificationIconAndColor: $k is duplicate: ${it.name} and ${old.name}") } else { @@ -100,20 +97,6 @@ enum class NotificationIconAndColor( } } -fun String.findNotificationIconAndColor() = - NotificationIconAndColor.map[this] - -fun PushMessage.notificationIconAndColor(): NotificationIconAndColor { - // mastodon - messageJson?.string("notification_type") - ?.findNotificationIconAndColor()?.let { return it } - - // misskey - when (messageJson?.string("type")) { - "notification" -> - messageJson?.jsonObject("body")?.string("type") - ?.findNotificationIconAndColor()?.let { return it } - } - - return NotificationIconAndColor.Unknown -} +fun PushMessage.iconColor() = + notificationType?.let { PushMessageIconColor.map[it] } + ?: PushMessageIconColor.Unknown diff --git a/app/src/main/java/jp/juggler/subwaytooter/push/PushRepo.kt b/app/src/main/java/jp/juggler/subwaytooter/push/PushRepo.kt index ea657ef5..04056629 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/push/PushRepo.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/push/PushRepo.kt @@ -4,11 +4,11 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.IconCompat import androidx.core.net.toUri import androidx.work.WorkManager import androidx.work.await -import jp.juggler.anko.BuildConfig import jp.juggler.crypt.* import jp.juggler.subwaytooter.ActCallback import jp.juggler.subwaytooter.R @@ -20,7 +20,7 @@ import jp.juggler.subwaytooter.api.push.ApiPushMisskey import jp.juggler.subwaytooter.dialog.SuspendProgress import jp.juggler.subwaytooter.notification.NotificationChannels import jp.juggler.subwaytooter.notification.NotificationDeleteReceiver.Companion.intentNotificationDelete -import jp.juggler.subwaytooter.notification.notificationIconAndColor +import jp.juggler.subwaytooter.notification.iconColor import jp.juggler.subwaytooter.pref.PrefDevice import jp.juggler.subwaytooter.pref.prefDevice import jp.juggler.subwaytooter.push.* @@ -637,7 +637,7 @@ class PushRepo( } val density = context.resources.displayMetrics.density - val iconAndColor = pm.notificationIconAndColor() + val iconAndColor = pm.iconColor() suspend fun PushMessage.loadSmallIcon(context: Context): IconCompat { iconSmall?.notEmpty() @@ -687,7 +687,7 @@ class PushRepo( // val piTap = PendingIntent.getActivity(this, nc.pircTap, iTap, PendingIntent.FLAG_IMMUTABLE) ncPushMessage.notify(context, urlDelete) { - color = iconAndColor.color + color = ContextCompat.getColor(context,iconAndColor.colorRes) setSmallIcon(iconSmall) iconBitmapLarge?.let { setLargeIcon(it) } setContentTitle(pm.loginAcct) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index acd32dbe..f99f6c6c 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -156,4 +156,20 @@ #707070 + + #e5e825 + #f57a33 + #f53333 + #60f516 + #33f59b + #40a9ff + #f5f233 + #39e3d5 + #ff3dbb + #f56a33 + #33f597 + #9433f5 + #ae1aed + +