Skip to content

Commit b06cb27

Browse files
authored
Merge pull request #386 from code-payments/chore/death-to-app-global-instance
chore: remove App.getInstance() used globally
2 parents 544cef2 + e137217 commit b06cb27

20 files changed

+234
-133
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.getcode.manager
2+
3+
import android.content.Context
4+
import com.getcode.crypt.MnemonicPhrase
5+
import com.getcode.solana.organizer.GiftCardAccount
6+
import dagger.hilt.android.qualifiers.ApplicationContext
7+
import javax.inject.Inject
8+
9+
class GiftCardManager @Inject constructor(
10+
@ApplicationContext private val context: Context
11+
) {
12+
fun createGiftCard(mnemonic: MnemonicPhrase? = null): GiftCardAccount {
13+
return GiftCardAccount.newInstance(context, mnemonic)
14+
}
15+
16+
fun getEntropy(giftCard: GiftCardAccount): String {
17+
return giftCard.mnemonicPhrase.getBase58EncodedEntropy(context)
18+
}
19+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.getcode.manager
2+
3+
import android.content.Context
4+
import com.getcode.crypt.MnemonicCode
5+
import com.getcode.crypt.MnemonicPhrase
6+
import com.getcode.ed25519.Ed25519.KeyPair
7+
import dagger.hilt.android.qualifiers.ApplicationContext
8+
import javax.inject.Inject
9+
10+
class MnemonicManager @Inject constructor(
11+
@ApplicationContext private val context: Context
12+
) {
13+
fun fromCashLink(cashLink: String): MnemonicPhrase {
14+
return MnemonicPhrase.fromEntropyB58(context, cashLink)
15+
}
16+
17+
fun fromEntropyBase64(entropy: String): MnemonicPhrase {
18+
return MnemonicPhrase.fromEntropyB64(context, entropy)
19+
}
20+
21+
fun getKeyPair(entropy: String): KeyPair {
22+
return fromEntropyBase64(entropy).getSolanaKeyPair(context)
23+
}
24+
25+
fun getKeyPair(mnemonicPhrase: MnemonicPhrase): KeyPair {
26+
return mnemonicPhrase.getSolanaKeyPair(context)
27+
}
28+
29+
fun getEncodedBase64(mnemonicPhrase: MnemonicPhrase): String {
30+
return mnemonicPhrase.getBase64EncodedEntropy(context)
31+
}
32+
33+
val mnemonicCode: MnemonicCode = context.resources.let(::MnemonicCode)
34+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.getcode.media
2+
3+
import android.content.Context
4+
import android.media.MediaScannerConnection
5+
import dagger.hilt.android.qualifiers.ApplicationContext
6+
import java.io.File
7+
import javax.inject.Inject
8+
9+
class MediaScanner @Inject constructor(
10+
@ApplicationContext private val context: Context
11+
) {
12+
fun scan(directory: File) {
13+
MediaScannerConnection.scanFile(context, arrayOf(directory.toString()), null, null)
14+
}
15+
}

app/src/main/java/com/getcode/App.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class App : Application() {
2222

2323
override fun onCreate() {
2424
super.onCreate()
25-
instance = this
2625

2726
CashBillAssets.load(this)
2827

@@ -63,13 +62,4 @@ class App : Application() {
6362
Bugsnag.start(this)
6463
}
6564
}
66-
67-
companion object {
68-
private lateinit var instance: Application
69-
70-
@Deprecated(message = "Anti-pattern; inject @ApplicationContext where needed. Convert objects to classes.")
71-
fun getInstance(): Application {
72-
return instance
73-
}
74-
}
7565
}

app/src/main/java/com/getcode/util/AccountUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ object AccountUtils {
7878
val subject = SingleSubject.create<Pair<String?, Account?>>()
7979
return subject.doOnSubscribe {
8080
CoroutineScope(Dispatchers.IO).launch {
81-
val result = getAccountNoActivity(App.getInstance())
81+
val result = getAccountNoActivity(context)
8282
subject.onSuccess(result ?: (null to null))
8383
}
8484
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.getcode.util
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import android.net.Uri
6+
import android.provider.Settings
7+
import androidx.core.content.ContextCompat
8+
import com.getcode.BuildConfig
9+
import com.getcode.utils.makeE164
10+
11+
fun Context.launchAppSettings() {
12+
ContextCompat.startActivity(
13+
this,
14+
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
15+
data = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
16+
flags = Intent.FLAG_ACTIVITY_NEW_TASK
17+
},
18+
null
19+
)
20+
}
21+
22+
fun Context.launchSmsIntent(phoneValue: String, message: String) {
23+
val uri: Uri = Uri.parse("smsto:${phoneValue.makeE164()}")
24+
val intent = Intent(Intent.ACTION_SENDTO, uri)
25+
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
26+
intent.putExtra(
27+
"sms_body",
28+
message
29+
)
30+
ContextCompat.startActivity(this, intent, null)
31+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.getcode.util
2+
3+
import android.content.Context
4+
import dagger.hilt.android.qualifiers.ApplicationContext
5+
import javax.inject.Inject
6+
7+
class IntentLauncher @Inject constructor(
8+
@ApplicationContext private val context: Context
9+
) {
10+
fun launchAppSettings() {
11+
context.launchAppSettings()
12+
}
13+
14+
fun launchSmsIntent(phoneValue: String, message: String) {
15+
context.launchSmsIntent(phoneValue, message)
16+
}
17+
}

app/src/main/java/com/getcode/util/IntentUtils.kt

Lines changed: 0 additions & 33 deletions
This file was deleted.

app/src/main/java/com/getcode/view/login/AccessKey.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ import com.getcode.navigation.core.LocalCodeNavigator
5454
import com.getcode.navigation.screens.LoginArgs
5555
import com.getcode.theme.CodeTheme
5656
import com.getcode.theme.White
57-
import com.getcode.util.IntentUtils
5857
import com.getcode.ui.utils.measured
5958
import com.getcode.ui.components.AccessKeySelectionContainer
6059
import com.getcode.ui.components.ButtonState
@@ -65,6 +64,7 @@ import com.getcode.ui.components.getPermissionLauncher
6564
import com.getcode.ui.components.rememberSelectionState
6665
import com.getcode.ui.utils.addIf
6766
import com.getcode.ui.utils.debugBounds
67+
import com.getcode.util.launchAppSettings
6868

6969
@OptIn(ExperimentalComposeUiApi::class)
7070
@Preview
@@ -93,7 +93,7 @@ fun AccessKey(
9393
message = context.getString(R.string.error_description_failedToSave),
9494
type = TopBarManager.TopBarMessageType.ERROR,
9595
secondaryText = context.getString(R.string.action_openSettings),
96-
secondaryAction = { IntentUtils.launchAppSettings() }
96+
secondaryAction = { context.launchAppSettings() }
9797
)
9898
)
9999
}

app/src/main/java/com/getcode/view/login/AccessKeyViewModel.kt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,35 @@ package com.getcode.view.login
33
import android.Manifest
44
import android.annotation.SuppressLint
55
import android.os.Build
6-
import dagger.hilt.android.lifecycle.HiltViewModel
7-
import com.getcode.App
8-
import com.getcode.crypt.MnemonicPhrase
96
import com.getcode.analytics.AnalyticsService
107
import com.getcode.manager.AuthManager
11-
import com.getcode.navigation.screens.CodeLoginPermission
8+
import com.getcode.media.MediaScanner
9+
import com.getcode.manager.MnemonicManager
1210
import com.getcode.navigation.core.CodeNavigator
11+
import com.getcode.navigation.screens.CodeLoginPermission
1312
import com.getcode.navigation.screens.HomeScreen
1413
import com.getcode.navigation.screens.LoginScreen
1514
import com.getcode.navigation.screens.PermissionRequestScreen
1615
import com.getcode.network.repository.getPublicKeyBase58
1716
import com.getcode.util.permissions.PermissionChecker
1817
import com.getcode.util.resources.ResourceHelper
19-
import javax.inject.Inject
18+
import dagger.hilt.android.lifecycle.HiltViewModel
2019
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
2120
import io.reactivex.rxjava3.core.Completable
2221
import io.reactivex.rxjava3.schedulers.Schedulers
2322
import java.util.concurrent.TimeUnit
23+
import javax.inject.Inject
2424

2525

2626
@HiltViewModel
2727
class AccessKeyViewModel @Inject constructor(
2828
private val authManager: AuthManager,
2929
private val analytics: AnalyticsService,
3030
private val permissions: PermissionChecker,
31+
private val mnemonicManager: MnemonicManager,
3132
resources: ResourceHelper,
32-
) : BaseAccessKeyViewModel(resources) {
33+
mediaScanner: MediaScanner,
34+
) : BaseAccessKeyViewModel(resources, mnemonicManager, mediaScanner) {
3335
@SuppressLint("CheckResult")
3436
fun onSubmit(navigator: CodeNavigator, isSaveImage: Boolean, isDeepLink: Boolean = false) {
3537
val entropyB64 = uiFlow.value.entropyB64 ?: return
@@ -69,8 +71,7 @@ class AccessKeyViewModel @Inject constructor(
6971
}
7072

7173
private fun onComplete(navigator: CodeNavigator, entropyB64: String) {
72-
val owner = MnemonicPhrase.fromEntropyB64(App.getInstance(), entropyB64)
73-
.getSolanaKeyPair(App.getInstance())
74+
val owner = mnemonicManager.getKeyPair(entropyB64)
7475
analytics.createAccount(true, owner.getPublicKeyBase58())
7576

7677
val cameraPermissionDenied = permissions.isDenied(Manifest.permission.CAMERA)

app/src/main/java/com/getcode/view/login/BaseAccessKeyViewModel.kt

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,22 @@ import android.graphics.Bitmap
44
import android.graphics.Canvas
55
import android.graphics.Paint
66
import android.graphics.Typeface
7-
import android.media.MediaScannerConnection
87
import android.os.Environment
98
import androidmads.library.qrgenearator.QRGContents
109
import androidmads.library.qrgenearator.QRGEncoder
1110
import androidx.core.graphics.applyCanvas
1211
import androidx.core.graphics.drawable.toBitmap
1312
import androidx.lifecycle.viewModelScope
14-
import com.getcode.App
1513
import com.getcode.R
16-
import com.getcode.crypt.MnemonicPhrase
14+
import com.getcode.media.MediaScanner
15+
import com.getcode.manager.MnemonicManager
1716
import com.getcode.manager.SessionManager
1817
import com.getcode.manager.TopBarManager
1918
import com.getcode.network.repository.ApiDeniedException
2019
import com.getcode.network.repository.decodeBase64
21-
import com.getcode.theme.*
20+
import com.getcode.theme.Brand
21+
import com.getcode.theme.Transparent
22+
import com.getcode.theme.White
2223
import com.getcode.ui.utils.toAGColor
2324
import com.getcode.util.resources.ResourceHelper
2425
import com.getcode.utils.ErrorUtils
@@ -34,7 +35,8 @@ import java.io.File
3435
import java.io.FileOutputStream
3536
import java.text.DateFormat
3637
import java.text.SimpleDateFormat
37-
import java.util.*
38+
import java.util.Date
39+
import java.util.Locale
3840

3941

4042
data class AccessKeyUiModel(
@@ -48,8 +50,11 @@ data class AccessKeyUiModel(
4850
val accessKeyCroppedBitmap: Bitmap? = null,
4951
)
5052

51-
abstract class BaseAccessKeyViewModel(private val resources: ResourceHelper) :
52-
BaseViewModel(resources) {
53+
abstract class BaseAccessKeyViewModel(
54+
private val resources: ResourceHelper,
55+
private val mnemonicManager: MnemonicManager,
56+
private val mediaScanner: MediaScanner,
57+
) : BaseViewModel(resources) {
5358
val uiFlow = MutableStateFlow(AccessKeyUiModel())
5459

5560
fun init() {
@@ -63,7 +68,7 @@ abstract class BaseAccessKeyViewModel(private val resources: ResourceHelper) :
6368
fun initWithEntropy(entropyB64: String) {
6469
if (uiFlow.value.entropyB64 == entropyB64) return
6570
Timber.d("entropy=$entropyB64")
66-
val words = MnemonicPhrase.fromEntropyB64(App.getInstance(), entropyB64).words
71+
val words = mnemonicManager.fromEntropyBase64(entropyB64).words
6772
val wordsFormatted = getAccessKeyText(words).joinToString("\n")
6873

6974
uiFlow.value = uiFlow.value.copy(
@@ -74,8 +79,10 @@ abstract class BaseAccessKeyViewModel(private val resources: ResourceHelper) :
7479

7580
CoroutineScope(Dispatchers.IO).launch {
7681
val accessKeyBitmap = createBitmapForExport(words = words, entropyB64 = entropyB64)
77-
val accessKeyBitmapDisplay = createBitmapForExport(drawBackground = false, words, entropyB64)
78-
val accessKeyCroppedBitmap = Bitmap.createBitmap(accessKeyBitmapDisplay, 0, 300, 1200, 1450)
82+
val accessKeyBitmapDisplay =
83+
createBitmapForExport(drawBackground = false, words, entropyB64)
84+
val accessKeyCroppedBitmap =
85+
Bitmap.createBitmap(accessKeyBitmapDisplay, 0, 300, 1200, 1450)
7986

8087
uiFlow.value = uiFlow.value.copy(
8188
accessKeyBitmap = accessKeyBitmap,
@@ -123,7 +130,7 @@ abstract class BaseAccessKeyViewModel(private val resources: ResourceHelper) :
123130
ErrorUtils.handleError(e)
124131
return false
125132
}
126-
MediaScannerConnection.scanFile(App.getInstance(), arrayOf(sd.toString()), null, null)
133+
mediaScanner.scan(sd)
127134
return true
128135
}
129136

app/src/main/java/com/getcode/view/login/CameraPermissionCheck.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import androidx.compose.ui.platform.LocalContext
66
import com.getcode.App
77
import com.getcode.R
88
import com.getcode.manager.TopBarManager
9-
import com.getcode.util.IntentUtils
109
import com.getcode.ui.components.PermissionCheck
1110
import com.getcode.ui.components.getPermissionLauncher
11+
import com.getcode.util.launchAppSettings
1212

1313
@Composable
1414
fun cameraPermissionCheck(isShowError: Boolean = true, onResult: (Boolean) -> Unit): (Boolean) -> Unit {
@@ -21,7 +21,7 @@ fun cameraPermissionCheck(isShowError: Boolean = true, onResult: (Boolean) -> Un
2121
message = context.getString(R.string.error_description_cameraAccessRequired),
2222
type = TopBarManager.TopBarMessageType.ERROR,
2323
secondaryText = context.getString(R.string.action_openSettings),
24-
secondaryAction = { IntentUtils.launchAppSettings() }
24+
secondaryAction = { context.launchAppSettings() }
2525
)
2626
)
2727
}

app/src/main/java/com/getcode/view/login/NotificationPermissionCheck.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import androidx.compose.ui.platform.LocalContext
77
import com.getcode.App
88
import com.getcode.R
99
import com.getcode.manager.TopBarManager
10-
import com.getcode.util.IntentUtils
1110
import com.getcode.ui.components.PermissionCheck
1211
import com.getcode.ui.components.getPermissionLauncher
12+
import com.getcode.util.launchAppSettings
1313

1414
@Composable
1515
fun notificationPermissionCheck(isShowError: Boolean = true, onResult: (Boolean) -> Unit): (Boolean) -> Unit {
@@ -22,7 +22,7 @@ fun notificationPermissionCheck(isShowError: Boolean = true, onResult: (Boolean)
2222
message = context.getString(R.string.permissions_description_push),
2323
type = TopBarManager.TopBarMessageType.ERROR,
2424
secondaryText = context.getString(R.string.action_openSettings),
25-
secondaryAction = { IntentUtils.launchAppSettings() }
25+
secondaryAction = { context.launchAppSettings() }
2626
)
2727
)
2828
}

0 commit comments

Comments
 (0)