BigTextStyleを使う。通知タップ時にアカウントの通知カラムを開く。

This commit is contained in:
tateisu 2023-02-06 01:34:51 +09:00
parent ba361806bd
commit 055b196be6
6 changed files with 84 additions and 13 deletions

View File

@ -140,7 +140,7 @@ class ActPushMessageList : AppCompatActivity() {
println("to: ${pm.loginAcct}") println("to: ${pm.loginAcct}")
println("type: ${pm.notificationType}") println("type: ${pm.notificationType}")
println("id: ${pm.notificationId}") println("id: ${pm.notificationId}")
println("text: ${pm.rawBody?.size}") println("text: ${pm.textExpand}")
println("dataSize: ${pm.rawBody?.size}") println("dataSize: ${pm.rawBody?.size}")
println("messageJson=${pm.messageJson?.toString(1, sort = true)}") println("messageJson=${pm.messageJson?.toString(1, sort = true)}")
@ -207,7 +207,7 @@ class ActPushMessageList : AppCompatActivity() {
"type: ${pm.notificationType}", "type: ${pm.notificationType}",
"id: ${pm.notificationId}", "id: ${pm.notificationId}",
"dataSize: ${pm.rawBody?.size}", "dataSize: ${pm.rawBody?.size}",
pm.text pm.textExpand
).mapNotNull { it.notBlank() }.joinToString("\n") ).mapNotNull { it.notBlank() }.joinToString("\n")
} }
} }

View File

@ -191,6 +191,8 @@ private fun ActMain.handleNotificationClick(uri: Uri, dataIdString: String) {
return return
} }
pushRepo.onTapNotification(account)
recycleClickedNotification(this, uri) recycleClickedNotification(this, uri)
val columnList = appState.columnList val columnList = appState.columnList

View File

@ -71,8 +71,8 @@ object PullNotification {
val params = listOf( val params = listOf(
"db_id" to account.db_id.toString(), "db_id" to account.db_id.toString(),
"type" to trackingType.str, "type" to trackingType.str, // URIをユニークにするため。参照されない
"notificationId" to notificationId "notificationId" to notificationId, // URIをユニークにするため。参照されない
).mapNotNull { ).mapNotNull {
when (val second = it.second) { when (val second = it.second) {
null -> null null -> null

View File

@ -161,6 +161,10 @@ class PushMastodon(
pm.text = arrayOf( pm.text = arrayOf(
// あなたのトゥートが tateisu 🤹 さんにお気に入り登録されました // あなたのトゥートが tateisu 🤹 さんにお気に入り登録されました
json.string("title"), json.string("title"),
).mapNotNull { it?.trim()?.notBlank() }.joinToString("\n")
pm.textExpand = arrayOf(
// あなたのトゥートが tateisu 🤹 さんにお気に入り登録されました
json.string("title"),
// 対象の投稿の本文? // 対象の投稿の本文?
json.string("body"), json.string("body"),
// 対象の投稿の本文? (古い // 対象の投稿の本文? (古い

View File

@ -2,11 +2,14 @@ package jp.juggler.subwaytooter.push
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.await import androidx.work.await
import jp.juggler.crypt.* import jp.juggler.crypt.*
import jp.juggler.subwaytooter.ActCallback
import jp.juggler.subwaytooter.R import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.entity.Acct import jp.juggler.subwaytooter.api.entity.Acct
import jp.juggler.subwaytooter.api.entity.Host import jp.juggler.subwaytooter.api.entity.Host
@ -25,6 +28,7 @@ import jp.juggler.subwaytooter.push.PushWorker.Companion.enqueueRegisterEndpoint
import jp.juggler.subwaytooter.table.* import jp.juggler.subwaytooter.table.*
import jp.juggler.subwaytooter.util.loadIcon import jp.juggler.subwaytooter.util.loadIcon
import jp.juggler.util.coroutine.AppDispatchers import jp.juggler.util.coroutine.AppDispatchers
import jp.juggler.util.coroutine.EmptyScope
import jp.juggler.util.data.* import jp.juggler.util.data.*
import jp.juggler.util.data.Base128.decodeBase128 import jp.juggler.util.data.Base128.decodeBase128
import jp.juggler.util.log.LogCategory import jp.juggler.util.log.LogCategory
@ -32,6 +36,7 @@ import jp.juggler.util.log.withCaption
import jp.juggler.util.os.applicationContextSafe import jp.juggler.util.os.applicationContextSafe
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.unifiedpush.android.connector.UnifiedPush import org.unifiedpush.android.connector.UnifiedPush
@ -526,7 +531,7 @@ class PushRepo(
} }
// 解読できた(例外が出なかった)なら通知を出す // 解読できた(例外が出なかった)なら通知を出す
showPushNotification(pm) showPushNotification(pm,account,notificationId)
} }
/** /**
@ -608,7 +613,11 @@ class PushRepo(
/** /**
* SNSからの通知を表示する * SNSからの通知を表示する
*/ */
private suspend fun showPushNotification(pm: PushMessage) { private suspend fun showPushNotification(
pm: PushMessage,
account: SavedAccount,
notificationId:String,
) {
if (ncPushMessage.isDissabled(context)) { if (ncPushMessage.isDissabled(context)) {
log.w("ncPushMessage isDissabled.") log.w("ncPushMessage isDissabled.")
return return
@ -628,8 +637,26 @@ class PushRepo(
val iconSmall = pm.loadSmallIcon(context) val iconSmall = pm.loadSmallIcon(context)
val iconBitmapLarge = context.loadIcon(pm.iconLarge, (48f * density + 0.5f).toInt()) val iconBitmapLarge = context.loadIcon(pm.iconLarge, (48f * density + 0.5f).toInt())
val urlTap = "subwaytooter://pushMessage/${pm.id}"
val iTap = context.intentNotificationDelete(urlTap.toUri()) val params = listOf(
"db_id" to account.db_id.toString(),
// URIをユニークにするため。参照されない
"type" to "v2push", // "type" to trackingType.str,
// URIをユニークにするため。参照されない
"notificationId" to notificationId,
).mapNotNull {
when (val second = it.second) {
null -> null
else -> "${it.first.encodePercent()}=${second.encodePercent()}"
}
}.joinToString("&")
val iTap = Intent(context, ActCallback::class.java).apply {
data = "subwaytooter://notification_click/?$params".toUri()
// FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY を付与してはいけない
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
val piTap = PendingIntent.getActivity( val piTap = PendingIntent.getActivity(
context, context,
ncPushMessage.pircTap, ncPushMessage.pircTap,
@ -660,6 +687,9 @@ class PushRepo(
setContentIntent(piTap) setContentIntent(piTap)
setDeleteIntent(piDelete) setDeleteIntent(piDelete)
setAutoCancel(true) setAutoCancel(true)
pm.textExpand.notEmpty()?.let {
setStyle(NotificationCompat.BigTextStyle().bigText(it))
}
} }
} }
@ -687,4 +717,18 @@ class PushRepo(
?: error("missing messageDbId in $uri") ?: error("missing messageDbId in $uri")
daoPushMessage.dismiss(messageDbId) daoPushMessage.dismiss(messageDbId)
} }
/**
* 通知タップのインテントをメイン画面が受け取った
*/
fun onTapNotification(account: SavedAccount) {
EmptyScope.launch(AppDispatchers.IO) {
try{
daoPushMessage.dismissByAcct(account.acct)
}catch(ex:Throwable){
log.e(ex,"onTapNotification failed.")
}
}
}
} }

View File

@ -5,6 +5,7 @@ import android.database.Cursor
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import android.provider.BaseColumns import android.provider.BaseColumns
import androidx.room.* import androidx.room.*
import jp.juggler.subwaytooter.api.entity.Acct
import jp.juggler.util.* import jp.juggler.util.*
import jp.juggler.util.data.* import jp.juggler.util.data.*
import jp.juggler.util.log.LogCategory import jp.juggler.util.log.LogCategory
@ -26,8 +27,10 @@ data class PushMessage(
var notificationId: String? = null, var notificationId: String? = null,
// 通知の種別。小アイコン、アクセント色、Misskeyの文言に影響する // 通知の種別。小アイコン、アクセント色、Misskeyの文言に影響する
var notificationType: String? = null, var notificationType: String? = null,
// 通知表示の本文 // 通知表示の本文
var text: String? = null, var text: String? = null,
// 展開表示した本文
var textExpand: String? = null,
// 小アイコンURL。昔のMastodonはバッジ画像が提供されていた。 // 小アイコンURL。昔のMastodonはバッジ画像が提供されていた。
var iconSmall: String? = null, var iconSmall: String? = null,
// 大アイコンURL。通知の原因となったユーザのアイコン画像。 // 大アイコンURL。通知の原因となったユーザのアイコン画像。
@ -51,6 +54,7 @@ data class PushMessage(
private const val COL_NOTIFICATION_ID = "notification_id" private const val COL_NOTIFICATION_ID = "notification_id"
private const val COL_NOTIFICATION_TYPE = "notification_type" private const val COL_NOTIFICATION_TYPE = "notification_type"
private const val COL_TEXT = "text" private const val COL_TEXT = "text"
private const val COL_TEXT_EXPAND = "text_expand"
private const val COL_ICON_SMALL = "icon_small" private const val COL_ICON_SMALL = "icon_small"
private const val COL_ICON_LARGE = "icon_large" private const val COL_ICON_LARGE = "icon_large"
private const val COL_MESSAGE_JSON = "message_json" private const val COL_MESSAGE_JSON = "message_json"
@ -61,17 +65,24 @@ data class PushMessage(
deleteBeforeCreate = true deleteBeforeCreate = true
column(0, COL_ID, MetaColumns.TS_INT_PRIMARY_KEY_NOT_NULL) column(0, COL_ID, MetaColumns.TS_INT_PRIMARY_KEY_NOT_NULL)
column(0, COL_LOGIN_ACCT, MetaColumns.TS_TEXT_NULL) column(0, COL_LOGIN_ACCT, MetaColumns.TS_TEXT_NULL)
column(0, COL_TIMESTAMP, MetaColumns.TS_ZERO) column(0, COL_TIMESTAMP, MetaColumns.TS_ZERO_NOT_NULL)
column(0, COL_TIME_SAVE, MetaColumns.TS_ZERO) column(0, COL_TIME_SAVE, MetaColumns.TS_ZERO_NOT_NULL)
column(0, COL_TIME_DISMISS, MetaColumns.TS_ZERO) column(0, COL_TIME_DISMISS, MetaColumns.TS_ZERO_NOT_NULL)
column(0, COL_NOTIFICATION_ID, MetaColumns.TS_TEXT_NULL) column(0, COL_NOTIFICATION_ID, MetaColumns.TS_TEXT_NULL)
column(0, COL_NOTIFICATION_TYPE, MetaColumns.TS_TEXT_NULL) column(0, COL_NOTIFICATION_TYPE, MetaColumns.TS_TEXT_NULL)
column(0, COL_TEXT, MetaColumns.TS_TEXT_NULL) column(0, COL_TEXT, MetaColumns.TS_TEXT_NULL)
column(0, COL_TEXT_EXPAND, MetaColumns.TS_TEXT_NULL)
column(0, COL_ICON_SMALL, MetaColumns.TS_TEXT_NULL) column(0, COL_ICON_SMALL, MetaColumns.TS_TEXT_NULL)
column(0, COL_ICON_LARGE, MetaColumns.TS_TEXT_NULL) column(0, COL_ICON_LARGE, MetaColumns.TS_TEXT_NULL)
column(0, COL_MESSAGE_JSON, MetaColumns.TS_TEXT_NULL) column(0, COL_MESSAGE_JSON, MetaColumns.TS_TEXT_NULL)
column(0, COL_HEADER_JSON, MetaColumns.TS_TEXT_NULL) column(0, COL_HEADER_JSON, MetaColumns.TS_TEXT_NULL)
column(0, COL_RAW_BODY, MetaColumns.TS_BLOB_NULL) column(0, COL_RAW_BODY, MetaColumns.TS_BLOB_NULL)
createExtra={
arrayOf(
"create index if not exists ${TABLE}_save on $TABLE($COL_TIME_SAVE)",
"create index if not exists ${TABLE}_acct_dismiss on $TABLE($COL_LOGIN_ACCT,$COL_TIME_DISMISS)",
)
}
} }
override fun onDBCreate(db: SQLiteDatabase) { override fun onDBCreate(db: SQLiteDatabase) {
@ -103,6 +114,7 @@ data class PushMessage(
val idxNotificationId = cursor.getColumnIndex(COL_NOTIFICATION_ID) val idxNotificationId = cursor.getColumnIndex(COL_NOTIFICATION_ID)
val idxNotificationType = cursor.getColumnIndex(COL_NOTIFICATION_TYPE) val idxNotificationType = cursor.getColumnIndex(COL_NOTIFICATION_TYPE)
val idxText = cursor.getColumnIndex(COL_TEXT) val idxText = cursor.getColumnIndex(COL_TEXT)
val idxTextExpand = cursor.getColumnIndex(COL_TEXT_EXPAND)
val idxIconSmall = cursor.getColumnIndex(COL_ICON_SMALL) val idxIconSmall = cursor.getColumnIndex(COL_ICON_SMALL)
val idxIconLarge = cursor.getColumnIndex(COL_ICON_LARGE) val idxIconLarge = cursor.getColumnIndex(COL_ICON_LARGE)
val idxMessageJson = cursor.getColumnIndex(COL_MESSAGE_JSON) val idxMessageJson = cursor.getColumnIndex(COL_MESSAGE_JSON)
@ -119,6 +131,7 @@ data class PushMessage(
notificationId = cursor.getStringOrNull(idxNotificationId), notificationId = cursor.getStringOrNull(idxNotificationId),
notificationType = cursor.getStringOrNull(idxNotificationType), notificationType = cursor.getStringOrNull(idxNotificationType),
text = cursor.getStringOrNull(idxText), text = cursor.getStringOrNull(idxText),
textExpand = cursor.getStringOrNull(idxTextExpand),
iconSmall = cursor.getStringOrNull(idxIconSmall), iconSmall = cursor.getStringOrNull(idxIconSmall),
iconLarge = cursor.getStringOrNull(idxIconLarge), iconLarge = cursor.getStringOrNull(idxIconLarge),
messageJson = cursor.getStringOrNull(idxMessageJson)?.decodeJsonObject(), messageJson = cursor.getStringOrNull(idxMessageJson)?.decodeJsonObject(),
@ -148,6 +161,7 @@ data class PushMessage(
put(COL_NOTIFICATION_ID, notificationId) put(COL_NOTIFICATION_ID, notificationId)
put(COL_NOTIFICATION_TYPE, notificationType) put(COL_NOTIFICATION_TYPE, notificationType)
put(COL_TEXT, text) put(COL_TEXT, text)
put(COL_TEXT_EXPAND, textExpand)
put(COL_ICON_SMALL, iconSmall) put(COL_ICON_SMALL, iconSmall)
put(COL_ICON_LARGE, iconLarge) put(COL_ICON_LARGE, iconLarge)
put(COL_MESSAGE_JSON, messageJson?.toString()) put(COL_MESSAGE_JSON, messageJson?.toString())
@ -192,6 +206,13 @@ data class PushMessage(
} }
} }
fun dismissByAcct(acct: Acct) {
db.execSQL(
"update $table set $COL_TIME_DISMISS=? where $COL_LOGIN_ACCT=? and $COL_TIME_DISMISS=0",
arrayOf(System.currentTimeMillis().toString(), acct.ascii),
)
}
fun deleteOld(now: Long) { fun deleteOld(now: Long) {
try { try {
val expire = now - TimeUnit.DAYS.toMillis(30) val expire = now - TimeUnit.DAYS.toMillis(30)
@ -202,7 +223,7 @@ data class PushMessage(
} }
} }
suspend fun listAll(): List<PushMessage> = fun listAll(): List<PushMessage> =
db.queryAll(TABLE, "$COL_TIME_SAVE desc") db.queryAll(TABLE, "$COL_TIME_SAVE desc")
?.use { ColIdx(it).readAll(it) } ?.use { ColIdx(it).readAll(it) }
?: emptyList() ?: emptyList()