From a902c63adf09a5c1fa7e78e11cef06373c163ae5 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Mon, 16 Oct 2023 17:34:13 -0300 Subject: [PATCH] created history screen --- .../nostrsigner/LocalPreferences.kt | 21 ++++++- .../greenart7c3/nostrsigner/SignerProvider.kt | 13 ++++ .../greenart7c3/nostrsigner/models/Account.kt | 9 ++- .../greenart7c3/nostrsigner/ui/HomeScreen.kt | 12 ++-- .../greenart7c3/nostrsigner/ui/MainScreen.kt | 63 ++++++++++++++++++- .../nostrsigner/ui/navigation/Route.kt | 8 +++ 6 files changed, 118 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt b/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt index 781bcf16..fb49029d 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt @@ -4,7 +4,10 @@ import android.annotation.SuppressLint import android.content.Context import android.content.SharedPreferences import androidx.compose.runtime.Immutable +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.greenart7c3.nostrsigner.models.Account +import com.greenart7c3.nostrsigner.models.History import com.greenart7c3.nostrsigner.service.KeyPair import com.vitorpamplona.quartz.encoders.hexToByteArray import com.vitorpamplona.quartz.encoders.toHexKey @@ -25,6 +28,7 @@ private object PrefKeys { const val NOSTR_PRIVKEY = "nostr_privkey" const val NOSTR_PUBKEY = "nostr_pubkey" const val REMEMBER_APPS = "remember_apps" + const val HISTORY = "history" const val ACCOUNT_NAME = "account_name" } @@ -185,6 +189,8 @@ object LocalPreferences { val jsonObject = JSONObject(account.savedApps.toMap()) putString(PrefKeys.REMEMBER_APPS, jsonObject.toString()) putString(PrefKeys.ACCOUNT_NAME, account.name) + val historyObject = Gson().toJson(account.history) + putString(PrefKeys.HISTORY, historyObject) }.apply() } @@ -204,6 +210,14 @@ object LocalPreferences { } } + fun loadHistory(npub: String?): MutableList { + encryptedPreferences(npub).apply { + val historyJson = getString(PrefKeys.HISTORY, "[]") ?: "[]" + val listType = object : TypeToken>() {}.type + return Gson().fromJson>(historyJson, listType).toMutableList() + } + } + fun loadFromEncryptedStorage(npub: String?): Account? { encryptedPreferences(npub).apply { val pubKey = getString(PrefKeys.NOSTR_PUBKEY, null) ?: return null @@ -222,10 +236,15 @@ object LocalPreferences { } val name = getString(PrefKeys.ACCOUNT_NAME, "") ?: "" + val historyJson = getString(PrefKeys.HISTORY, "[]") ?: "[]" + val listType = object : TypeToken>() {}.type + val history = Gson().fromJson>(historyJson, listType).toMutableList() + return Account( keyPair = KeyPair(privKey = privKey?.hexToByteArray(), pubKey = pubKey.hexToByteArray()), name = name, - savedApps = outputMap + savedApps = outputMap, + history = history ) } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt b/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt index 1c3ef24b..8e42acbf 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/SignerProvider.kt @@ -5,7 +5,9 @@ import android.content.ContentValues import android.database.Cursor import android.database.MatrixCursor import android.net.Uri +import com.greenart7c3.nostrsigner.models.History import com.greenart7c3.nostrsigner.models.SignerType +import com.greenart7c3.nostrsigner.models.TimeUtils import com.greenart7c3.nostrsigner.service.AmberUtils import com.greenart7c3.nostrsigner.service.model.AmberEvent import com.vitorpamplona.quartz.encoders.toNpub @@ -61,6 +63,10 @@ class SignerProvider : ContentProvider() { AmberEvent.fromJson(json), account.keyPair.privKey ) + + account.history.add(History(packageName, "SIGN_EVENT", TimeUtils.now())) + LocalPreferences.saveToEncryptedStorage(account) + val cursor = MatrixCursor(arrayOf("signature", "event")) cursor.addRow(arrayOf(signedEvent.sig, signedEvent.toJson())) return cursor @@ -99,6 +105,10 @@ class SignerProvider : ContentProvider() { } catch (e: Exception) { "Could not decrypt the message" } + + account.history.add(History(packageName, stringType, TimeUtils.now())) + LocalPreferences.saveToEncryptedStorage(account) + if (type == SignerType.NIP04_ENCRYPT && result == "Could not decrypt the message") { return null } else { @@ -115,6 +125,9 @@ class SignerProvider : ContentProvider() { val isRemembered = account.savedApps[key] ?: false if (!isRemembered) return null + account.history.add(History(packageName, "GET_PUBLIC_KEY", TimeUtils.now())) + LocalPreferences.saveToEncryptedStorage(account) + val cursor = MatrixCursor(arrayOf("signature")) cursor.addRow(arrayOf(account.keyPair.pubKey.toNpub())) return cursor diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/Account.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/Account.kt index 3372a827..70e27faa 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/Account.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/Account.kt @@ -5,11 +5,18 @@ import androidx.compose.runtime.Stable import androidx.lifecycle.LiveData import com.greenart7c3.nostrsigner.service.KeyPair +class History( + val appName: String, + val type: String, + val time: Long +) + @Stable class Account( val keyPair: KeyPair, var name: String, - var savedApps: MutableMap + var savedApps: MutableMap, + var history: MutableList = mutableListOf() ) { val saveable: AccountLiveData = AccountLiveData(this) } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/HomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/HomeScreen.kt index e3f763f9..b604ad2f 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/HomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/HomeScreen.kt @@ -82,7 +82,8 @@ fun HomeScreen( sig, "", sig, - it.callBackUrl + it.callBackUrl, + it.type ) } return@LoginWithPubKey @@ -157,7 +158,8 @@ fun HomeScreen( result, it.id, result, - it.callBackUrl + it.callBackUrl, + it.type ) } } @@ -247,7 +249,8 @@ fun HomeScreen( resultEvent.toJson(), it.id, resultEvent.toJson(), - it.callBackUrl + it.callBackUrl, + it.type ) } } else { @@ -267,7 +270,8 @@ fun HomeScreen( signedEvent.toJson(), it.id, signedEvent.sig, - it.callBackUrl + it.callBackUrl, + it.type ) } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt index d84984e5..2d4dc9aa 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt @@ -4,17 +4,22 @@ import android.app.Activity.RESULT_OK import android.content.Context import android.content.Intent import android.net.Uri +import android.text.format.DateUtils import android.widget.Toast import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandMore +import androidx.compose.material3.Card import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -35,6 +40,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.toLowerCase import androidx.compose.ui.unit.dp import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -45,7 +52,10 @@ import androidx.navigation.navArgument import com.greenart7c3.nostrsigner.LocalPreferences import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.models.Account +import com.greenart7c3.nostrsigner.models.History import com.greenart7c3.nostrsigner.models.IntentData +import com.greenart7c3.nostrsigner.models.SignerType +import com.greenart7c3.nostrsigner.models.TimeUtils import com.greenart7c3.nostrsigner.service.getAppCompatActivity import com.greenart7c3.nostrsigner.service.toShortenHex import com.greenart7c3.nostrsigner.ui.actions.AccountsBottomSheet @@ -66,13 +76,15 @@ fun sendResult( event: String, id: String, value: String, - callBackUrl: String? + callBackUrl: String?, + type: SignerType ) { val activity = context.getAppCompatActivity() if (packageName != null) { if (rememberChoice) { account.savedApps[key] = rememberChoice } + account.history.add(History(packageName, type.toString(), TimeUtils.now())) LocalPreferences.saveToEncryptedStorage(account) val intent = Intent() intent.putExtra("signature", value) @@ -114,7 +126,7 @@ fun MainScreen( ) { val navController = rememberNavController() val navBackStackEntry by navController.currentBackStackEntryAsState() - val items = listOf(Route.Home, Route.Permissions, Route.Settings) + val items = listOf(Route.Home, Route.Permissions, Route.History, Route.Settings) var shouldShowBottomSheet by remember { mutableStateOf(false) } val sheetState = rememberModalBottomSheetState( @@ -237,6 +249,37 @@ fun MainScreen( } ) + composable( + Route.History.route, + content = { + val history = LocalPreferences.loadHistory(account.keyPair.pubKey.toNpub()) + LazyColumn( + Modifier + .fillMaxSize() + .padding(padding) + .padding(16.dp) + ) { + items(history.size) { + Card( + Modifier + .fillMaxWidth() + .padding(4.dp) + ) { + Column( + Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + Text(history[it].appName) + Text(history[it].type.toLowerCase(Locale.current)) + Text(timeAgoShort(history[it].time, "now")) + } + } + } + } + } + ) + composable( Route.Permission.route, arguments = listOf(navArgument("packageName") { type = NavType.StringType }), @@ -255,3 +298,19 @@ fun MainScreen( } } } + +fun timeAgoShort(mills: Long?, stringForNow: String): String { + if (mills == null) return " " + + var humanReadable = DateUtils.getRelativeTimeSpanString( + mills * 1000, + System.currentTimeMillis(), + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_ALL + ).toString() + if (humanReadable.startsWith("In") || humanReadable.startsWith("0")) { + humanReadable = stringForNow + } + + return humanReadable +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt index bb6d4a3b..c2cd3be0 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt @@ -1,9 +1,11 @@ package com.greenart7c3.nostrsigner.ui.navigation import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.History import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.ViewList +import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.Home import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.ViewList @@ -39,4 +41,10 @@ sealed class Route( icon = Icons.Outlined.ViewList, selectedIcon = Icons.Default.ViewList ) + + data object History : Route( + route = "History", + icon = Icons.Outlined.History, + selectedIcon = Icons.Default.History + ) }