mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 06:18:26 +00:00
Merge branch 'pr/1451' into feature/compose-cleanup
This commit is contained in:
commit
666a04c432
@ -125,7 +125,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.home.HomeActivity"
|
android:name="org.thoughtcrime.securesms.home.HomeActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="standard"
|
||||||
android:theme="@style/Theme.Session.DayNight.NoActionBar" />
|
android:theme="@style/Theme.Session.DayNight.NoActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity"
|
android:name="org.thoughtcrime.securesms.messagerequests.MessageRequestsActivity"
|
||||||
|
@ -49,17 +49,20 @@ internal fun StartConversationScreen(
|
|||||||
ItemButton(
|
ItemButton(
|
||||||
textId = R.string.messageNew,
|
textId = R.string.messageNew,
|
||||||
icon = R.drawable.ic_message,
|
icon = R.drawable.ic_message,
|
||||||
|
modifier = Modifier.contentDescription(R.string.AccessibilityId_new_direct_message),
|
||||||
onClick = delegate::onNewMessageSelected)
|
onClick = delegate::onNewMessageSelected)
|
||||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||||
ItemButton(
|
ItemButton(
|
||||||
textId = R.string.activity_create_group_title,
|
textId = R.string.activity_create_group_title,
|
||||||
icon = R.drawable.ic_group,
|
icon = R.drawable.ic_group,
|
||||||
|
modifier = Modifier.contentDescription(R.string.AccessibilityId_create_group),
|
||||||
onClick = delegate::onCreateGroupSelected
|
onClick = delegate::onCreateGroupSelected
|
||||||
)
|
)
|
||||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||||
ItemButton(
|
ItemButton(
|
||||||
textId = R.string.dialog_join_community_title,
|
textId = R.string.dialog_join_community_title,
|
||||||
icon = R.drawable.ic_globe,
|
icon = R.drawable.ic_globe,
|
||||||
|
modifier = Modifier.contentDescription(R.string.AccessibilityId_join_community),
|
||||||
onClick = delegate::onJoinCommunitySelected
|
onClick = delegate::onJoinCommunitySelected
|
||||||
)
|
)
|
||||||
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
Divider(startIndent = LocalDimensions.current.dividerIndent)
|
||||||
|
@ -138,6 +138,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
|||||||
// region Lifecycle
|
// region Lifecycle
|
||||||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
||||||
super.onCreate(savedInstanceState, isReady)
|
super.onCreate(savedInstanceState, isReady)
|
||||||
|
|
||||||
|
if (!isTaskRoot) { finish(); return }
|
||||||
|
|
||||||
// Set content view
|
// Set content view
|
||||||
binding = ActivityHomeBinding.inflate(layoutInflater)
|
binding = ActivityHomeBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package org.thoughtcrime.securesms.onboarding
|
package org.thoughtcrime.securesms.onboarding
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
@ -11,12 +12,13 @@ import org.thoughtcrime.securesms.ui.theme.LocalColors
|
|||||||
@Composable
|
@Composable
|
||||||
fun OnboardingBackPressAlertDialog(
|
fun OnboardingBackPressAlertDialog(
|
||||||
dismissDialog: () -> Unit,
|
dismissDialog: () -> Unit,
|
||||||
|
@StringRes textId: Int = R.string.you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit,
|
||||||
quit: () -> Unit
|
quit: () -> Unit
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = dismissDialog,
|
onDismissRequest = dismissDialog,
|
||||||
title = stringResource(R.string.warning),
|
title = stringResource(R.string.warning),
|
||||||
text = stringResource(R.string.you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit),
|
text = stringResource(textId),
|
||||||
buttons = listOf(
|
buttons = listOf(
|
||||||
DialogButtonModel(
|
DialogButtonModel(
|
||||||
GetString(stringResource(R.string.quit)),
|
GetString(stringResource(R.string.quit)),
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package org.thoughtcrime.securesms.onboarding.loading
|
package org.thoughtcrime.securesms.onboarding.loading
|
||||||
|
|
||||||
import androidx.compose.animation.core.Animatable
|
|
||||||
import androidx.compose.animation.core.TweenSpec
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -20,21 +16,11 @@ import org.thoughtcrime.securesms.ui.contentDescription
|
|||||||
import org.thoughtcrime.securesms.ui.theme.h7
|
import org.thoughtcrime.securesms.ui.theme.h7
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun LoadingScreen(state: State) {
|
internal fun LoadingScreen(progress: Float) {
|
||||||
val animatable = remember { Animatable(initialValue = 0f, visibilityThreshold = 0.005f) }
|
|
||||||
|
|
||||||
LaunchedEffect(state) {
|
|
||||||
animatable.stop()
|
|
||||||
animatable.animateTo(
|
|
||||||
targetValue = 1f,
|
|
||||||
animationSpec = TweenSpec(durationMillis = state.duration.inWholeMilliseconds.toInt())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
ProgressArc(
|
ProgressArc(
|
||||||
animatable.value,
|
progress,
|
||||||
modifier = Modifier.contentDescription(R.string.AccessibilityId_loading_animation)
|
modifier = Modifier.contentDescription(R.string.AccessibilityId_loading_animation)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
|
@ -47,8 +47,8 @@ class LoadingActivity: BaseActionBarActivity() {
|
|||||||
ApplicationContext.getInstance(this).newAccount = false
|
ApplicationContext.getInstance(this).newAccount = false
|
||||||
|
|
||||||
setComposeContent {
|
setComposeContent {
|
||||||
val state by viewModel.states.collectAsState()
|
val progress by viewModel.progress.collectAsState()
|
||||||
LoadingScreen(state)
|
LoadingScreen(progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
@ -4,15 +4,20 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.buffer
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.flow.timeout
|
import kotlinx.coroutines.flow.timeout
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -23,25 +28,43 @@ import kotlin.time.Duration
|
|||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
data class State(val duration: Duration)
|
enum class State {
|
||||||
|
LOADING,
|
||||||
|
SUCCESS,
|
||||||
|
FAIL
|
||||||
|
}
|
||||||
|
|
||||||
private val ANIMATE_TO_DONE_TIME = 500.milliseconds
|
private val ANIMATE_TO_DONE_TIME = 500.milliseconds
|
||||||
private val IDLE_DONE_TIME = 1.seconds
|
private val IDLE_DONE_TIME = 1.seconds
|
||||||
private val TIMEOUT_TIME = 15.seconds
|
private val TIMEOUT_TIME = 15.seconds
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
private val REFRESH_TIME = 50.milliseconds
|
||||||
|
|
||||||
|
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class LoadingViewModel @Inject constructor(
|
internal class LoadingViewModel @Inject constructor(
|
||||||
val prefs: TextSecurePreferences
|
val prefs: TextSecurePreferences
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
|
|
||||||
private val _states = MutableStateFlow(State(TIMEOUT_TIME))
|
private val state = MutableStateFlow(State.LOADING)
|
||||||
val states = _states.asStateFlow()
|
|
||||||
|
private val _progress = MutableStateFlow(0f)
|
||||||
|
val progress = _progress.asStateFlow()
|
||||||
|
|
||||||
private val _events = MutableSharedFlow<Event>()
|
private val _events = MutableSharedFlow<Event>()
|
||||||
val events = _events.asSharedFlow()
|
val events = _events.asSharedFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
state.flatMapLatest {
|
||||||
|
when (it) {
|
||||||
|
State.LOADING -> progress(0f, 1f, TIMEOUT_TIME)
|
||||||
|
else -> progress(progress.value, 1f, ANIMATE_TO_DONE_TIME)
|
||||||
|
}
|
||||||
|
}.buffer(0, BufferOverflow.DROP_OLDEST)
|
||||||
|
.collectLatest { _progress.value = it }
|
||||||
|
}
|
||||||
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
TextSecurePreferences.events
|
TextSecurePreferences.events
|
||||||
@ -58,7 +81,7 @@ internal class LoadingViewModel @Inject constructor(
|
|||||||
|
|
||||||
private suspend fun onSuccess() {
|
private suspend fun onSuccess() {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
_states.value = State(ANIMATE_TO_DONE_TIME)
|
state.value = State.SUCCESS
|
||||||
delay(IDLE_DONE_TIME)
|
delay(IDLE_DONE_TIME)
|
||||||
_events.emit(Event.SUCCESS)
|
_events.emit(Event.SUCCESS)
|
||||||
}
|
}
|
||||||
@ -66,6 +89,8 @@ internal class LoadingViewModel @Inject constructor(
|
|||||||
|
|
||||||
private suspend fun onFail() {
|
private suspend fun onFail() {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
state.value = State.FAIL
|
||||||
|
delay(IDLE_DONE_TIME)
|
||||||
_events.emit(Event.TIMEOUT)
|
_events.emit(Event.TIMEOUT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,3 +100,22 @@ sealed interface Event {
|
|||||||
object SUCCESS: Event
|
object SUCCESS: Event
|
||||||
object TIMEOUT: Event
|
object TIMEOUT: Event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun progress(
|
||||||
|
init: Float,
|
||||||
|
target: Float,
|
||||||
|
time: Duration,
|
||||||
|
refreshRate: Duration = REFRESH_TIME
|
||||||
|
): Flow<Float> = flow {
|
||||||
|
val startMs = System.currentTimeMillis()
|
||||||
|
val timeMs = time.inWholeMilliseconds
|
||||||
|
val finishMs = startMs + timeMs
|
||||||
|
val range = target - init
|
||||||
|
|
||||||
|
generateSequence { System.currentTimeMillis() }.takeWhile { it < finishMs }.forEach {
|
||||||
|
emit((it - startMs) * range / timeMs + init)
|
||||||
|
delay(refreshRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(target)
|
||||||
|
}
|
||||||
|
@ -55,7 +55,7 @@ internal fun MessageNotificationsScreen(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.showDialog) OnboardingBackPressAlertDialog(dismissDialog, quit)
|
if (state.showDialog) OnboardingBackPressAlertDialog(dismissDialog, quit = quit)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
@ -40,7 +40,11 @@ internal fun PickDisplayName(
|
|||||||
quit: () -> Unit = {}
|
quit: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (state.showDialog) OnboardingBackPressAlertDialog(dismissDialog, quit)
|
if (state.showDialog) OnboardingBackPressAlertDialog(
|
||||||
|
dismissDialog,
|
||||||
|
R.string.you_cannot_go_back_further_cancel_account_creation,
|
||||||
|
quit
|
||||||
|
)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -293,6 +293,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
|
|||||||
val userConfig = configFactory.user
|
val userConfig = configFactory.user
|
||||||
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!), profilePicture)
|
||||||
prefs.setProfileAvatarId(profilePicture?.let { SecureRandom().nextInt() } ?: 0 )
|
prefs.setProfileAvatarId(profilePicture?.let { SecureRandom().nextInt() } ?: 0 )
|
||||||
|
|
||||||
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey)
|
||||||
// new config
|
// new config
|
||||||
val url = TextSecurePreferences.getProfilePictureURL(this)
|
val url = TextSecurePreferences.getProfilePictureURL(this)
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
<string name="AccessibilityId_new_conversation_button">New conversation button</string>
|
<string name="AccessibilityId_new_conversation_button">New conversation button</string>
|
||||||
<string name="AccessibilityId_new_direct_message">New direct message</string>
|
<string name="AccessibilityId_new_direct_message">New direct message</string>
|
||||||
<string name="AccessibilityId_create_group">Create group</string>
|
<string name="AccessibilityId_create_group">Create group</string>
|
||||||
<string name="AccessibilityId_join_community">Join community button</string>
|
<string name="AccessibilityId_join_community">Join community</string>
|
||||||
<!-- Join community pop up -->
|
<!-- Join community pop up -->
|
||||||
<string name="AccessibilityId_community_input_box">Community input</string>
|
<string name="AccessibilityId_community_input_box">Community input</string>
|
||||||
<string name="AccessibilityId_join_community_button">Join community button</string>
|
<string name="AccessibilityId_join_community_button">Join community button</string>
|
||||||
@ -1140,5 +1140,6 @@
|
|||||||
<string name="AccessibilityId_qr_code">QR code</string>
|
<string name="AccessibilityId_qr_code">QR code</string>
|
||||||
<string name="warning">Warning</string>
|
<string name="warning">Warning</string>
|
||||||
<string name="you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit">You cannot go back further. In order to stop loading your account, Session needs to quit.</string>
|
<string name="you_cannot_go_back_further_in_order_to_stop_loading_your_account_session_needs_to_quit">You cannot go back further. In order to stop loading your account, Session needs to quit.</string>
|
||||||
|
<string name="you_cannot_go_back_further_cancel_account_creation">You cannot go back further. In order to cancel your account creation, Session needs to quit.</string>
|
||||||
<string name="quit">Quit</string>
|
<string name="quit">Quit</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -21,8 +21,8 @@ googleServicesVersion=4.3.12
|
|||||||
kotlinVersion=1.8.21
|
kotlinVersion=1.8.21
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
appcompatVersion=1.6.1
|
appcompatVersion=1.6.1
|
||||||
composeVersion=1.6.4
|
|
||||||
coreVersion=1.13.1
|
coreVersion=1.13.1
|
||||||
|
composeVersion=1.6.4
|
||||||
coroutinesVersion=1.6.4
|
coroutinesVersion=1.6.4
|
||||||
curve25519Version=0.6.0
|
curve25519Version=0.6.0
|
||||||
daggerVersion=2.46.1
|
daggerVersion=2.46.1
|
||||||
|
@ -35,14 +35,14 @@ class MnemonicCodecTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `decode one invalid word that is too short`() {
|
fun `decode one invalid word that is too short`() {
|
||||||
assertThrows(InvalidWord::class.java) {
|
assertThrows(InputTooShort::class.java) {
|
||||||
codec.decode("a")
|
codec.decode("a")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `decode one invalid word`() {
|
fun `decode one invalid word`() {
|
||||||
assertThrows(InvalidWord::class.java) {
|
assertThrows(InputTooShort::class.java) {
|
||||||
codec.decode("abcd")
|
codec.decode("abcd")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,12 +96,20 @@ class MnemonicCodecTest {
|
|||||||
|
|
||||||
assertEquals("0f2ccde528622876b8f16e14db97dafc", result)
|
assertEquals("0f2ccde528622876b8f16e14db97dafc", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `decodeMnemonicOrHexAsByteArray with account id throws`() {
|
||||||
|
assertThrows(InputTooShort::class.java) {
|
||||||
|
codec.decodeMnemonicOrHexAsByteArray("0582e1421da6f584a4795d30b654b4f25fed860afdf081075cb26a2b997e492f14").let(Hex::toStringCondensed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `decodeMnemonicOrHexAsByteArray with bad hex`() {
|
fun `decodeMnemonicOrHexAsByteArray with bad hex`() {
|
||||||
// throws InvalidWord as 0f2ccde528622876b8f16e14db97dafcg is not a valid word on the english wordlist.
|
// throws InvalidWord as 0f2ccde528622876b8f16e14db97dafcg is not a valid word on the english wordlist.
|
||||||
// It is also not a valid hex string, but we assume that a non-hex string is a recovery password.
|
// It is also not a valid hex string, but we assume that a non-hex string is a recovery password.
|
||||||
|
|
||||||
assertThrows(InvalidWord::class.java) {
|
assertThrows(InputTooShort::class.java) {
|
||||||
codec.decodeMnemonicOrHexAsByteArray("0f2ccde528622876b8f16e14db97dafcg").let(Hex::toStringCondensed)
|
codec.decodeMnemonicOrHexAsByteArray("0f2ccde528622876b8f16e14db97dafcg").let(Hex::toStringCondensed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,8 +83,8 @@ class MnemonicCodec(private val loadFileContents: (String) -> String) {
|
|||||||
val prefixLength = languageConfiguration.prefixLength
|
val prefixLength = languageConfiguration.prefixLength
|
||||||
val n = truncatedWordSet.size.toLong()
|
val n = truncatedWordSet.size.toLong()
|
||||||
|
|
||||||
if (mnemonic.isEmpty()) throw DecodingError.InputTooShort
|
// Check preconditions
|
||||||
if (words.isEmpty()) throw DecodingError.InputTooShort
|
if (words.size < 13) throw DecodingError.InputTooShort
|
||||||
|
|
||||||
fun String.prefix() = substring(0 until prefixLength)
|
fun String.prefix() = substring(0 until prefixLength)
|
||||||
|
|
||||||
@ -96,9 +96,6 @@ class MnemonicCodec(private val loadFileContents: (String) -> String) {
|
|||||||
val wordIndexes = wordPrefixes.map { truncatedWordSet.indexOf(it) }
|
val wordIndexes = wordPrefixes.map { truncatedWordSet.indexOf(it) }
|
||||||
.onEach { if (it < 0) throw DecodingError.InvalidWord }
|
.onEach { if (it < 0) throw DecodingError.InvalidWord }
|
||||||
|
|
||||||
// Check preconditions
|
|
||||||
if (words.size < 13) throw DecodingError.InputTooShort
|
|
||||||
|
|
||||||
// Verify checksum
|
// Verify checksum
|
||||||
val checksumIndex = determineChecksumIndex(words.dropLast(1), prefixLength)
|
val checksumIndex = determineChecksumIndex(words.dropLast(1), prefixLength)
|
||||||
val expectedChecksumWord = words[checksumIndex]
|
val expectedChecksumWord = words[checksumIndex]
|
||||||
@ -128,13 +125,12 @@ class MnemonicCodec(private val loadFileContents: (String) -> String) {
|
|||||||
decode(mnemonic = mnemonicOrHex).let(Hex::fromStringCondensed)
|
decode(mnemonic = mnemonicOrHex).let(Hex::fromStringCondensed)
|
||||||
} catch (decodeException: Exception) {
|
} catch (decodeException: Exception) {
|
||||||
// It's not a valid mnemonic, if it's pure-hexadecimal then we'll interpret it as a
|
// It's not a valid mnemonic, if it's pure-hexadecimal then we'll interpret it as a
|
||||||
// hexadecimal-byte encoded mnemonic.
|
// hexadecimal-byte encoded mnemonic... unless it's 66 chars or longer, then it could be
|
||||||
if (!mnemonicOrHex.isHex()) throw decodeException
|
// an account id.
|
||||||
try {
|
mnemonicOrHex.takeIf { it.length < 66 && it.isHex() }
|
||||||
Hex.fromStringCondensed(mnemonicOrHex)
|
.runCatching { Hex.fromStringCondensed(this) }
|
||||||
} catch (_: Exception) {
|
.getOrNull()
|
||||||
throw decodeException
|
?: throw decodeException
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user