diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 1f4e6b5bd..955c91675 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -78,6 +78,7 @@ dependencies { implementation(Libs.grpc_okhttp) implementation(Libs.grpc_kotlin) + implementation(Libs.androidx_lifecycle_runtime) implementation(Libs.androidx_room_runtime) implementation(Libs.androidx_room_ktx) implementation(Libs.androidx_room_rxjava3) diff --git a/api/src/main/java/com/getcode/manager/MnemonicManager.kt b/api/src/main/java/com/getcode/manager/MnemonicManager.kt index 5df4e6421..01b5132e5 100644 --- a/api/src/main/java/com/getcode/manager/MnemonicManager.kt +++ b/api/src/main/java/com/getcode/manager/MnemonicManager.kt @@ -30,5 +30,9 @@ class MnemonicManager @Inject constructor( return mnemonicPhrase.getBase64EncodedEntropy(context) } + fun getEncodedBase58(mnemonicPhrase: MnemonicPhrase): String { + return mnemonicPhrase.getBase58EncodedEntropy(context) + } + val mnemonicCode: MnemonicCode = context.resources.let(::MnemonicCode) } \ No newline at end of file diff --git a/api/src/main/java/com/getcode/network/TipController.kt b/api/src/main/java/com/getcode/network/TipController.kt index 8d14d3536..66ffb01f5 100644 --- a/api/src/main/java/com/getcode/network/TipController.kt +++ b/api/src/main/java/com/getcode/network/TipController.kt @@ -1,15 +1,13 @@ package com.getcode.network -import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver -import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleOwner import com.getcode.manager.SessionManager import com.getcode.model.CodePayload -import com.getcode.model.TipMetadata import com.getcode.model.PrefsBool import com.getcode.model.PrefsString +import com.getcode.model.TipMetadata import com.getcode.model.TwitterUser import com.getcode.network.client.Client import com.getcode.network.client.fetchTwitterUser diff --git a/api/src/main/java/com/getcode/network/client/Client.kt b/api/src/main/java/com/getcode/network/client/Client.kt index 32f5d0adc..17b0c661b 100644 --- a/api/src/main/java/com/getcode/network/client/Client.kt +++ b/api/src/main/java/com/getcode/network/client/Client.kt @@ -1,30 +1,27 @@ package com.getcode.network.client -import android.content.Context -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.LifecycleOwner import com.getcode.analytics.AnalyticsService +import com.getcode.manager.MnemonicManager import com.getcode.manager.SessionManager import com.getcode.network.BalanceController -import com.getcode.network.HistoryController import com.getcode.network.exchange.Exchange import com.getcode.network.repository.AccountRepository import com.getcode.network.repository.IdentityRepository import com.getcode.network.repository.MessagingRepository import com.getcode.network.repository.PrefRepository import com.getcode.network.repository.TransactionRepository -import com.getcode.utils.network.NetworkConnectivityListener import com.getcode.network.service.ChatService import com.getcode.network.service.DeviceService import com.getcode.utils.ErrorUtils -import dagger.hilt.android.qualifiers.ApplicationContext +import com.getcode.utils.network.NetworkConnectivityListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel -import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import timber.log.Timber import java.util.Timer @@ -36,8 +33,6 @@ internal const val TAG = "Client" @Singleton class Client @Inject constructor( - @ApplicationContext - internal val context: Context, internal val identityRepository: IdentityRepository, internal val transactionRepository: TransactionRepository, internal val messagingRepository: MessagingRepository, @@ -51,9 +46,9 @@ class Client @Inject constructor( internal val networkObserver: NetworkConnectivityListener, internal val chatService: ChatService, internal val deviceService: DeviceService, -) : LifecycleEventObserver { + internal val mnemonicManager: MnemonicManager, +) { - private val TAG = "PollTimer" private val scope = CoroutineScope(Dispatchers.IO) private var pollTimer: Timer? = null @@ -62,13 +57,15 @@ class Client @Inject constructor( private fun startPollTimerWhenAuthenticated() { Timber.tag(TAG).i("Creating poll timer") scope.launch { - SessionManager.authState.collect { - if (it.isAuthenticated == true) { + SessionManager.authState + .map { it.isAuthenticated } + .filterNotNull() + .filter { it } + .onEach { Timber.tag(TAG).i("User Authenticated - starting timer") startPollTimer() this.cancel() - } - } + }.launchIn(this) } } @@ -101,28 +98,12 @@ class Client @Inject constructor( } } - private fun startTimer() { + fun startTimer() { startPollTimerWhenAuthenticated() } - fun pollOnce(delay: Long = 2_000L) { - scope.launch { - delay(delay) - Timber.tag(TAG).i("Poll Once") - poll() - } - } - - private fun stopTimer() { + fun stopTimer() { Timber.tag(TAG).i("Cancelling Poller") pollTimer?.cancel() } - - override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { - when (event) { - Lifecycle.Event.ON_RESUME -> startTimer() - Lifecycle.Event.ON_STOP -> stopTimer() - else -> Unit - } - } } \ No newline at end of file diff --git a/api/src/main/java/com/getcode/network/client/Client_Transaction.kt b/api/src/main/java/com/getcode/network/client/Client_Transaction.kt index fa0a0e0a2..53c210e28 100644 --- a/api/src/main/java/com/getcode/network/client/Client_Transaction.kt +++ b/api/src/main/java/com/getcode/network/client/Client_Transaction.kt @@ -96,7 +96,7 @@ fun Client.transferWithResultSingle( return getTransferPreflightAction(amount.kin) .andThen(Single.defer { transactionRepository.transfer( - context, amount, fee, additionalFees, organizer, rendezvousKey, destination, isWithdrawal, tipMetadata + amount, fee, additionalFees, organizer, rendezvousKey, destination, isWithdrawal, tipMetadata ) }) .map { @@ -141,7 +141,6 @@ fun Client.sendRemotely( getTransferPreflightAction(truncatedAmount.kin) .andThen( sendRemotely( - context = context, amount = truncatedAmount, organizer = organizer, rendezvousKey = rendezvousKey, @@ -150,7 +149,7 @@ fun Client.sendRemotely( .doOnComplete { val giftCardItem = GiftCard( key = giftCard.cluster.vaultPublicKey.base58(), - entropy = giftCard.mnemonicPhrase.getBase58EncodedEntropy(context), + entropy = mnemonicManager.getEncodedBase58(giftCard.mnemonicPhrase), amount = truncatedAmount.kin.quarks, date = System.currentTimeMillis() ) @@ -367,7 +366,6 @@ private fun Client.withdraw( } fun Client.sendRemotely( - context: Context, amount: KinAmount, organizer: Organizer, rendezvousKey: PublicKey, @@ -375,7 +373,7 @@ fun Client.sendRemotely( ): Completable { return Completable.defer { transactionRepository.sendRemotely( - context, amount, organizer, rendezvousKey, giftCard + amount, organizer, rendezvousKey, giftCard ) .map { if (it is IntentRemoteSend) { diff --git a/api/src/main/java/com/getcode/network/repository/TransactionRepository.kt b/api/src/main/java/com/getcode/network/repository/TransactionRepository.kt index cfe8a36f0..be44f6843 100644 --- a/api/src/main/java/com/getcode/network/repository/TransactionRepository.kt +++ b/api/src/main/java/com/getcode/network/repository/TransactionRepository.kt @@ -128,7 +128,6 @@ class TransactionRepository @Inject constructor( } fun transfer( - context: Context, amount: KinAmount, fee: Kin, additionalFees: List, @@ -254,7 +253,6 @@ class TransactionRepository @Inject constructor( } fun sendRemotely( - context: Context, amount: KinAmount, organizer: Organizer, rendezvousKey: PublicKey, diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e05884612..c3abe784a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -37,6 +37,8 @@ android { buildToolsVersion = Android.buildToolsVersion testInstrumentationRunner = Android.testInstrumentationRunner + resValue("string", "applicationId", Android.namespace) + buildConfigField("String", "MIXPANEL_API_KEY", "\"${tryReadProperty(rootProject.rootDir, "MIXPANEL_API_KEY")}\"") buildConfigField("String", "KADO_API_KEY", "\"${tryReadProperty(rootProject.rootDir, "KADO_API_KEY")}\"") buildConfigField("Boolean", "NOTIFY_ERRORS", "false") diff --git a/app/src/main/java/com/getcode/CodeApp.kt b/app/src/main/java/com/getcode/CodeApp.kt index f83c81dac..6bde791fc 100644 --- a/app/src/main/java/com/getcode/CodeApp.kt +++ b/app/src/main/java/com/getcode/CodeApp.kt @@ -200,36 +200,7 @@ internal data object MainRoot : Screen { Box( modifier = Modifier .fillMaxSize() - .background(CodeTheme.colors.background), - contentAlignment = Alignment.Center - ) { - var show by remember { - mutableStateOf(false) - } - - AnimatedContent(show, transitionSpec = { fadeIn() togetherWith fadeOut() }) { - if (it) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.inset) - ) { - Image( - painter = painterResource(R.drawable.ic_code_logo_near_white), - contentDescription = "", - modifier = Modifier - .fillMaxWidth(0.65f) - .fillMaxHeight(0.65f) - ) - - CodeCircularProgressIndicator() - } - } - } - - LaunchedEffect(Unit) { - delay(1_000) - show = true - } - } + .background(CodeTheme.colors.background) + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/getcode/inject/ApiModule.kt b/app/src/main/java/com/getcode/inject/ApiModule.kt index f19f2ce2b..1150a533c 100644 --- a/app/src/main/java/com/getcode/inject/ApiModule.kt +++ b/app/src/main/java/com/getcode/inject/ApiModule.kt @@ -4,6 +4,7 @@ import android.content.Context import com.getcode.BuildConfig import com.getcode.R import com.getcode.analytics.AnalyticsService +import com.getcode.manager.MnemonicManager import com.getcode.model.Currency import com.getcode.network.BalanceController import com.getcode.network.PrivacyMigration @@ -73,12 +74,8 @@ object ApiModule { val DEV_URL = "api.codeinfra.dev" val PROD_URL = "api.codeinfra.net" - return AndroidChannelBuilder.usingBuilder( - OkHttpChannelBuilderForcedTls12.forAddress( - PROD_URL, - TLS_PORT - ) - ) + return AndroidChannelBuilder + .usingBuilder(OkHttpChannelBuilderForcedTls12.forAddress(PROD_URL, TLS_PORT)) .context(context) .userAgent("Code/Android/${BuildConfig.VERSION_NAME}") .keepAliveTime(4, TimeUnit.MINUTES) @@ -87,7 +84,9 @@ object ApiModule { @Singleton @Provides - fun provideAccountAuthenticator(@ApplicationContext context: Context): AccountAuthenticator { + fun provideAccountAuthenticator( + @ApplicationContext context: Context, + ): AccountAuthenticator { return AccountAuthenticator(context) } @@ -157,7 +156,6 @@ object ApiModule { @Singleton @Provides fun provideClient( - @ApplicationContext context: Context, identityRepository: IdentityRepository, transactionRepository: TransactionRepository, messagingRepository: MessagingRepository, @@ -171,9 +169,9 @@ object ApiModule { networkObserver: NetworkConnectivityListener, chatService: ChatService, deviceService: DeviceService, + mnemonicManager: MnemonicManager, ): Client { return Client( - context, identityRepository, transactionRepository, messagingRepository, @@ -187,6 +185,7 @@ object ApiModule { networkObserver, chatService, deviceService, + mnemonicManager ) } diff --git a/app/src/main/java/com/getcode/util/AccountAuthenticator.kt b/app/src/main/java/com/getcode/util/AccountAuthenticator.kt index c4c3a0337..87d034d3a 100644 --- a/app/src/main/java/com/getcode/util/AccountAuthenticator.kt +++ b/app/src/main/java/com/getcode/util/AccountAuthenticator.kt @@ -3,10 +3,14 @@ package com.getcode.util import android.accounts.* import android.content.Context import android.os.Bundle +import com.getcode.model.PrefsString +import com.getcode.network.repository.PrefRepository import com.getcode.utils.startupLog -class AccountAuthenticator(private val mContext: Context) : AbstractAccountAuthenticator(mContext) { +class AccountAuthenticator( + private val context: Context, +) : AbstractAccountAuthenticator(context) { @Throws(NetworkErrorException::class) override fun addAccount( response: AccountAuthenticatorResponse, @@ -32,9 +36,9 @@ class AccountAuthenticator(private val mContext: Context) : AbstractAccountAuthe options: Bundle ): Bundle { // Extract the username and password from the Account Manager, then, generate token - val am = AccountManager.get(mContext) + val am = AccountManager.get(context) var authToken = am.peekAuthToken(account, authTokenType) - startupLog("authenticator: authToken ${authToken != null}") + startupLog("authenticator: authToken ${authToken != null}, $authTokenType") // Lets give another try to authenticate the user if (null != authToken) { if (authToken.isEmpty()) { @@ -56,7 +60,10 @@ class AccountAuthenticator(private val mContext: Context) : AbstractAccountAuthe } } - startupLog("authenticator failure", Throwable("Failed to retrieve authToken from AccountManager")) + startupLog( + "authenticator failure", + Throwable("Failed to retrieve authToken from AccountManager") + ) // If we get here, then we couldn't access the user's password return Bundle() } diff --git a/app/src/main/java/com/getcode/view/MainActivity.kt b/app/src/main/java/com/getcode/view/MainActivity.kt index 1886465a6..a3edd1c28 100644 --- a/app/src/main/java/com/getcode/view/MainActivity.kt +++ b/app/src/main/java/com/getcode/view/MainActivity.kt @@ -14,6 +14,7 @@ import com.getcode.LocalExchange import com.getcode.LocalNetworkObserver import com.getcode.LocalPhoneFormatter import com.getcode.analytics.AnalyticsService +import com.getcode.network.client.Client import com.getcode.network.exchange.Exchange import com.getcode.ui.utils.handleUncaughtException import com.getcode.util.CurrencyUtils @@ -30,6 +31,9 @@ import javax.inject.Inject @AndroidEntryPoint class MainActivity : FragmentActivity() { + @Inject + lateinit var client: Client + @Inject lateinit var analyticsManager: AnalyticsService @@ -96,5 +100,15 @@ class MainActivity : FragmentActivity() { private fun setFullscreen() { enableEdgeToEdge() } + + override fun onResume() { + super.onResume() + client.startTimer() + } + + override fun onStop() { + super.onStop() + client.stopTimer() + } } diff --git a/app/src/main/res/xml/authenticator.xml b/app/src/main/res/xml/authenticator.xml index 0c8a24a10..415269cbc 100644 --- a/app/src/main/res/xml/authenticator.xml +++ b/app/src/main/res/xml/authenticator.xml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 94f60be0e..7f3177ee2 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -22,7 +22,7 @@ object Versions { const val androidx_camerax = "1.3.2" const val androidx_core = "1.12.0" const val androidx_constraint_layout = "2.1.3" - const val androidx_lifecycle = "2.6.2" + const val androidx_lifecycle = "2.7.0" const val androidx_navigation = "2.7.4" const val androidx_browser = "1.4.0" const val androidx_paging = "3.2.1"