mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-17 07:23:21 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c423d93247 | ||
|
|
b3bb4c4861 | ||
|
|
a7b584a974 | ||
|
|
d62191ca3c | ||
|
|
4250dce563 | ||
|
|
ebe952bb66 | ||
|
|
0407961e4d | ||
|
|
d521a81a6e |
@@ -47,14 +47,14 @@ ktlint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 1226
|
def canonicalVersionCode = 1226
|
||||||
def canonicalVersionName = "6.13.6"
|
def canonicalVersionName = "6.13.7"
|
||||||
|
|
||||||
def postFixSize = 100
|
def postFixSize = 100
|
||||||
def abiPostFix = ['universal' : 0,
|
def abiPostFix = ['universal' : 5,
|
||||||
'armeabi-v7a' : 1,
|
'armeabi-v7a' : 6,
|
||||||
'arm64-v8a' : 2,
|
'arm64-v8a' : 7,
|
||||||
'x86' : 3,
|
'x86' : 8,
|
||||||
'x86_64' : 4]
|
'x86_64' : 9]
|
||||||
|
|
||||||
def keystores = [ 'debug' : loadKeystoreProperties('keystore.debug.properties') ]
|
def keystores = [ 'debug' : loadKeystoreProperties('keystore.debug.properties') ]
|
||||||
|
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||||||
.addBlocking("app-dependencies", this::initializeAppDependencies)
|
.addBlocking("app-dependencies", this::initializeAppDependencies)
|
||||||
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
|
.addBlocking("first-launch", this::initializeFirstEverAppLaunch)
|
||||||
.addBlocking("app-migrations", this::initializeApplicationMigrations)
|
.addBlocking("app-migrations", this::initializeApplicationMigrations)
|
||||||
.addBlocking("mark-registration", () -> RegistrationUtil.maybeMarkRegistrationComplete(this))
|
.addBlocking("mark-registration", () -> RegistrationUtil.maybeMarkRegistrationComplete())
|
||||||
.addBlocking("lifecycle-observer", () -> ApplicationDependencies.getAppForegroundObserver().addListener(this))
|
.addBlocking("lifecycle-observer", () -> ApplicationDependencies.getAppForegroundObserver().addListener(this))
|
||||||
.addBlocking("message-retriever", this::initializeMessageRetrieval)
|
.addBlocking("message-retriever", this::initializeMessageRetrieval)
|
||||||
.addBlocking("dynamic-theme", () -> DynamicTheme.setDefaultDayNightMode(this))
|
.addBlocking("dynamic-theme", () -> DynamicTheme.setDefaultDayNightMode(this))
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.thoughtcrime.securesms.components.registration
|
package org.thoughtcrime.securesms.components.registration
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
@@ -9,6 +11,7 @@ import org.thoughtcrime.securesms.R
|
|||||||
class VerificationCodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) :
|
class VerificationCodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) :
|
||||||
FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
|
FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
|
||||||
private val containers: MutableList<TextInputLayout> = ArrayList(6)
|
private val containers: MutableList<TextInputLayout> = ArrayList(6)
|
||||||
|
private val textWatcher = PasteTextWatcher()
|
||||||
private var listener: OnCodeEnteredListener? = null
|
private var listener: OnCodeEnteredListener? = null
|
||||||
private var index = 0
|
private var index = 0
|
||||||
|
|
||||||
@@ -22,6 +25,7 @@ class VerificationCodeView @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
containers.add(findViewById(R.id.container_five))
|
containers.add(findViewById(R.id.container_five))
|
||||||
|
|
||||||
containers.forEach { it.editText?.showSoftInputOnFocus = false }
|
containers.forEach { it.editText?.showSoftInputOnFocus = false }
|
||||||
|
containers.forEach { it.editText?.addTextChangedListener(textWatcher) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnCompleteListener(listener: OnCodeEnteredListener?) {
|
fun setOnCompleteListener(listener: OnCodeEnteredListener?) {
|
||||||
@@ -41,8 +45,9 @@ class VerificationCodeView @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun delete() {
|
fun delete() {
|
||||||
if (index <= 0) return
|
if (index < 0) return
|
||||||
containers[--index].editText?.setText("")
|
val editText = if (index == 0) containers[index].editText else containers[--index].editText
|
||||||
|
editText?.setText("")
|
||||||
containers[index].editText?.requestFocus()
|
containers[index].editText?.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,4 +62,29 @@ class VerificationCodeView @JvmOverloads constructor(context: Context, attrs: At
|
|||||||
interface OnCodeEnteredListener {
|
interface OnCodeEnteredListener {
|
||||||
fun onCodeComplete(code: String)
|
fun onCodeComplete(code: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class PasteTextWatcher : TextWatcher {
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
if (s == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.length > 1) {
|
||||||
|
val enteredText = s.toList()
|
||||||
|
enteredText.forEach {
|
||||||
|
val castInt = it.digitToIntOrNull()
|
||||||
|
if (castInt == null) {
|
||||||
|
s.clear()
|
||||||
|
return@forEach
|
||||||
|
} else {
|
||||||
|
append(castInt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ object ContactDiscovery {
|
|||||||
|
|
||||||
if (!SignalStore.registrationValues().isRegistrationComplete) {
|
if (!SignalStore.registrationValues().isRegistrationComplete) {
|
||||||
Log.w(TAG, "Registration is not yet complete. Skipping, but running a routine to possibly mark it complete.")
|
Log.w(TAG, "Registration is not yet complete. Skipping, but running a routine to possibly mark it complete.")
|
||||||
RegistrationUtil.maybeMarkRegistrationComplete(context)
|
RegistrationUtil.maybeMarkRegistrationComplete()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ public abstract class BaseKbsPinFragment<ViewModel extends BaseKbsPinViewModel>
|
|||||||
|
|
||||||
private void onPinSkipped() {
|
private void onPinSkipped() {
|
||||||
PinOptOutDialog.show(requireContext(), () -> {
|
PinOptOutDialog.show(requireContext(), () -> {
|
||||||
RegistrationUtil.maybeMarkRegistrationComplete(requireContext());
|
RegistrationUtil.maybeMarkRegistrationComplete();
|
||||||
closeNavGraphBranch();
|
closeNavGraphBranch();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,12 +85,12 @@ internal class ConfirmKbsPinFragment : BaseKbsPinFragment<ConfirmKbsPinViewModel
|
|||||||
confirm.cancelSpinning()
|
confirm.cancelSpinning()
|
||||||
requireActivity().setResult(Activity.RESULT_OK)
|
requireActivity().setResult(Activity.RESULT_OK)
|
||||||
closeNavGraphBranch()
|
closeNavGraphBranch()
|
||||||
RegistrationUtil.maybeMarkRegistrationComplete(requireContext())
|
RegistrationUtil.maybeMarkRegistrationComplete()
|
||||||
StorageSyncHelper.scheduleSyncForDataChange()
|
StorageSyncHelper.scheduleSyncForDataChange()
|
||||||
}
|
}
|
||||||
SaveAnimation.FAILURE -> {
|
SaveAnimation.FAILURE -> {
|
||||||
confirm.cancelSpinning()
|
confirm.cancelSpinning()
|
||||||
RegistrationUtil.maybeMarkRegistrationComplete(requireContext())
|
RegistrationUtil.maybeMarkRegistrationComplete()
|
||||||
displayFailedDialog()
|
displayFailedDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ public class PinRestoreEntryFragment extends LoggingFragment {
|
|||||||
profile.putExtra("next_intent", main);
|
profile.putExtra("next_intent", main);
|
||||||
startActivity(profile);
|
startActivity(profile);
|
||||||
} else {
|
} else {
|
||||||
RegistrationUtil.maybeMarkRegistrationComplete(requireContext());
|
RegistrationUtil.maybeMarkRegistrationComplete();
|
||||||
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
|
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
|
||||||
startActivity(MainActivity.clearTop(activity));
|
startActivity(MainActivity.clearTop(activity));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ public class EditSelfProfileRepository implements EditProfileRepository {
|
|||||||
.then(Arrays.asList(new MultiDeviceProfileKeyUpdateJob(), new MultiDeviceProfileContentUpdateJob()))
|
.then(Arrays.asList(new MultiDeviceProfileKeyUpdateJob(), new MultiDeviceProfileContentUpdateJob()))
|
||||||
.enqueue();
|
.enqueue();
|
||||||
|
|
||||||
RegistrationUtil.maybeMarkRegistrationComplete(context);
|
RegistrationUtil.maybeMarkRegistrationComplete();
|
||||||
|
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
SignalStore.misc().markHasEverHadAnAvatar();
|
SignalStore.misc().markHasEverHadAnAvatar();
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ public class AccountManagerFactory {
|
|||||||
public static @NonNull SignalServiceAccountManager createAuthenticated(@NonNull Context context,
|
public static @NonNull SignalServiceAccountManager createAuthenticated(@NonNull Context context,
|
||||||
@NonNull ACI aci,
|
@NonNull ACI aci,
|
||||||
@NonNull PNI pni,
|
@NonNull PNI pni,
|
||||||
@NonNull String number,
|
@NonNull String e164,
|
||||||
int deviceId,
|
int deviceId,
|
||||||
@NonNull String password)
|
@NonNull String password)
|
||||||
{
|
{
|
||||||
if (ApplicationDependencies.getSignalServiceNetworkAccess().isCensored(number)) {
|
if (ApplicationDependencies.getSignalServiceNetworkAccess().isCensored(e164)) {
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
try {
|
try {
|
||||||
ProviderInstaller.installIfNeeded(context);
|
ProviderInstaller.installIfNeeded(context);
|
||||||
@@ -36,10 +36,10 @@ public class AccountManagerFactory {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SignalServiceAccountManager(ApplicationDependencies.getSignalServiceNetworkAccess().getConfiguration(number),
|
return new SignalServiceAccountManager(ApplicationDependencies.getSignalServiceNetworkAccess().getConfiguration(e164),
|
||||||
aci,
|
aci,
|
||||||
pni,
|
pni,
|
||||||
number,
|
e164,
|
||||||
deviceId,
|
deviceId,
|
||||||
password,
|
password,
|
||||||
BuildConfig.SIGNAL_AGENT,
|
BuildConfig.SIGNAL_AGENT,
|
||||||
@@ -51,11 +51,11 @@ public class AccountManagerFactory {
|
|||||||
* Should only be used during registration when you haven't yet been assigned an ACI.
|
* Should only be used during registration when you haven't yet been assigned an ACI.
|
||||||
*/
|
*/
|
||||||
public static @NonNull SignalServiceAccountManager createUnauthenticated(@NonNull Context context,
|
public static @NonNull SignalServiceAccountManager createUnauthenticated(@NonNull Context context,
|
||||||
@NonNull String number,
|
@NonNull String e164,
|
||||||
int deviceId,
|
int deviceId,
|
||||||
@NonNull String password)
|
@NonNull String password)
|
||||||
{
|
{
|
||||||
if (new SignalServiceNetworkAccess(context).isCensored(number)) {
|
if (new SignalServiceNetworkAccess(context).isCensored(e164)) {
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
try {
|
try {
|
||||||
ProviderInstaller.installIfNeeded(context);
|
ProviderInstaller.installIfNeeded(context);
|
||||||
@@ -65,10 +65,10 @@ public class AccountManagerFactory {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SignalServiceAccountManager(ApplicationDependencies.getSignalServiceNetworkAccess().getConfiguration(number),
|
return new SignalServiceAccountManager(ApplicationDependencies.getSignalServiceNetworkAccess().getConfiguration(e164),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
number,
|
e164,
|
||||||
deviceId,
|
deviceId,
|
||||||
password,
|
password,
|
||||||
BuildConfig.SIGNAL_AGENT,
|
BuildConfig.SIGNAL_AGENT,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.push
|
package org.thoughtcrime.securesms.push
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
||||||
import okhttp3.CipherSuite
|
import okhttp3.CipherSuite
|
||||||
import okhttp3.ConnectionSpec
|
import okhttp3.ConnectionSpec
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
@@ -17,7 +18,6 @@ import org.thoughtcrime.securesms.net.RemoteDeprecationDetectorInterceptor
|
|||||||
import org.thoughtcrime.securesms.net.SequentialDns
|
import org.thoughtcrime.securesms.net.SequentialDns
|
||||||
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor
|
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor
|
||||||
import org.thoughtcrime.securesms.net.StaticDns
|
import org.thoughtcrime.securesms.net.StaticDns
|
||||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
|
||||||
import org.thoughtcrime.securesms.util.Base64
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
import org.whispersystems.signalservice.api.push.TrustStore
|
import org.whispersystems.signalservice.api.push.TrustStore
|
||||||
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
|
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
|
||||||
@@ -228,12 +228,12 @@ open class SignalServiceNetworkAccess(context: Context) {
|
|||||||
return getConfiguration(SignalStore.account().e164)
|
return getConfiguration(SignalStore.account().e164)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getConfiguration(localNumber: String?): SignalServiceConfiguration {
|
open fun getConfiguration(e164: String?): SignalServiceConfiguration {
|
||||||
if (localNumber == null || SignalStore.proxy().isProxyEnabled) {
|
if (e164 == null || SignalStore.proxy().isProxyEnabled) {
|
||||||
return uncensoredConfiguration
|
return uncensoredConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
val countryCode: Int = PhoneNumberFormatter.getLocalCountryCode()
|
val countryCode: Int = PhoneNumberUtil.getInstance().parse(e164, null).countryCode
|
||||||
|
|
||||||
return when (SignalStore.settings().censorshipCircumventionEnabled) {
|
return when (SignalStore.settings().censorshipCircumventionEnabled) {
|
||||||
SettingsValues.CensorshipCircumventionEnabled.ENABLED -> {
|
SettingsValues.CensorshipCircumventionEnabled.ENABLED -> {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public final class PushChallengeRequest {
|
|||||||
@NonNull Optional<String> fcmToken,
|
@NonNull Optional<String> fcmToken,
|
||||||
long timeoutMs)
|
long timeoutMs)
|
||||||
{
|
{
|
||||||
if (!fcmToken.isPresent()) {
|
if (fcmToken.isEmpty() || fcmToken.get().isEmpty()) {
|
||||||
Log.w(TAG, "Push challenge not requested, as no FCM token was present");
|
Log.w(TAG, "Push challenge not requested, as no FCM token was present");
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ data class RegistrationData(
|
|||||||
val pniRegistrationId: Int,
|
val pniRegistrationId: Int,
|
||||||
val recoveryPassword: String?
|
val recoveryPassword: String?
|
||||||
) {
|
) {
|
||||||
val isFcm: Boolean = fcmToken != null
|
val isNotFcm: Boolean = fcmToken.isNullOrBlank()
|
||||||
val isNotFcm: Boolean = fcmToken == null
|
val isFcm: Boolean = !isNotFcm
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,10 +218,10 @@ public final class RegistrationRepository {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<BackupAuthCheckProcessor> getKbsAuthCredential(@NonNull RegistrationData registrationData) {
|
public Single<BackupAuthCheckProcessor> getKbsAuthCredential(@NonNull RegistrationData registrationData, List<String> usernamePasswords) {
|
||||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(context, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
|
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(context, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
|
||||||
|
|
||||||
return accountManager.checkBackupAuthCredentials(registrationData.getE164(), SignalStore.kbsValues().getKbsAuthTokenList())
|
return accountManager.checkBackupAuthCredentials(registrationData.getE164(), usernamePasswords)
|
||||||
.map(BackupAuthCheckProcessor::new)
|
.map(BackupAuthCheckProcessor::new)
|
||||||
.doOnSuccess(processor -> {
|
.doOnSuccess(processor -> {
|
||||||
if (SignalStore.kbsValues().removeAuthTokens(processor.getInvalid())) {
|
if (SignalStore.kbsValues().removeAuthTokens(processor.getInvalid())) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.whispersystems.signalservice.api.util.Preconditions
|
|||||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||||
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
|
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
|
||||||
|
import org.whispersystems.signalservice.internal.push.RegistrationSessionState
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,7 +28,7 @@ sealed class RegistrationSessionProcessor(response: ServiceResponse<Registration
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override fun captchaRequired(): Boolean {
|
public override fun captchaRequired(): Boolean {
|
||||||
return super.captchaRequired() || (hasResult() && CAPTCHA_KEY == getChallenge())
|
return super.captchaRequired() || (hasResult() && CAPTCHA_KEY == getChallenge(emptyList()))
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun rateLimit(): Boolean {
|
public override fun rateLimit(): Boolean {
|
||||||
@@ -38,8 +39,9 @@ sealed class RegistrationSessionProcessor(response: ServiceResponse<Registration
|
|||||||
return super.getError()
|
return super.getError()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pushChallengeRequired(): Boolean {
|
fun pushChallengeTimedOut(): Boolean {
|
||||||
return PUSH_CHALLENGE_KEY == getChallenge()
|
val state: RegistrationSessionState = response.result.get().state ?: return false
|
||||||
|
return state.pushChallengeTimedOut
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isTokenRejected(): Boolean {
|
fun isTokenRejected(): Boolean {
|
||||||
@@ -107,9 +109,15 @@ sealed class RegistrationSessionProcessor(response: ServiceResponse<Registration
|
|||||||
return result.body.allowedToRequestCode
|
return result.body.allowedToRequestCode
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getChallenge(): String? {
|
/**
|
||||||
|
* Parse the response body for the server requested challenges that the client may submit.
|
||||||
|
*
|
||||||
|
* @param exclusions a collection of keys to ignore, used when they've already tried and failed
|
||||||
|
* @return the next challenge
|
||||||
|
*/
|
||||||
|
fun getChallenge(exclusions: Collection<String>): String? {
|
||||||
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
|
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
|
||||||
return result.body.requestedInformation.firstOrNull { REQUESTABLE_INFORMATION.contains(it) }
|
return result.body.requestedInformation.filterNot { exclusions.contains(it) }.firstOrNull { REQUESTABLE_INFORMATION.contains(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isVerified(): Boolean {
|
fun isVerified(): Boolean {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public final class RegistrationUtil {
|
|||||||
* path a user has taken. This will only truly mark registration as complete if all of the
|
* path a user has taken. This will only truly mark registration as complete if all of the
|
||||||
* requirements are met.
|
* requirements are met.
|
||||||
*/
|
*/
|
||||||
public static void maybeMarkRegistrationComplete(@NonNull Context context) {
|
public static void maybeMarkRegistrationComplete() {
|
||||||
if (!SignalStore.registrationValues().isRegistrationComplete() &&
|
if (!SignalStore.registrationValues().isRegistrationComplete() &&
|
||||||
SignalStore.account().isRegistered() &&
|
SignalStore.account().isRegistered() &&
|
||||||
!Recipient.self().getProfileName().isEmpty() &&
|
!Recipient.self().getProfileName().isEmpty() &&
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
|||||||
import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException
|
import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||||
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
|
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
|
||||||
|
import org.whispersystems.signalservice.internal.push.RegistrationSessionState
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
@@ -84,7 +85,9 @@ class VerifyAccountRepository(private val context: Application) {
|
|||||||
return if (challenge != null) {
|
return if (challenge != null) {
|
||||||
accountManager.submitPushChallengeToken(response.result.get().body.id, challenge)
|
accountManager.submitPushChallengeToken(response.result.get().body.id, challenge)
|
||||||
} else {
|
} else {
|
||||||
response
|
val registrationSessionState = RegistrationSessionState(pushChallengeTimedOut = true)
|
||||||
|
val rawResponse: RegistrationSessionMetadataResponse = response.result.get()
|
||||||
|
ServiceResponse.forResult(rawResponse.copy(state = registrationSessionState), 200, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -384,8 +384,7 @@ public abstract class BaseEnterSmsCodeFragment<ViewModel extends BaseRegistratio
|
|||||||
|
|
||||||
subheader.setText(requireContext().getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, viewModel.getNumber().getFullFormattedNumber()));
|
subheader.setText(requireContext().getString(R.string.RegistrationActivity_enter_the_code_we_sent_to_s, viewModel.getNumber().getFullFormattedNumber()));
|
||||||
|
|
||||||
MccMncProducer mccMncProducer = new MccMncProducer(requireContext());
|
Disposable request = viewModel.validateSession(sessionE164)
|
||||||
Disposable request = viewModel.validateSession(sessionE164, mccMncProducer.getMcc(), mccMncProducer.getMnc())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(processor -> {
|
.subscribe(processor -> {
|
||||||
if (!processor.hasResult()) {
|
if (!processor.hasResult()) {
|
||||||
|
|||||||
@@ -355,15 +355,14 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
String sessionE164 = viewModel.getSessionE164();
|
String sessionE164 = viewModel.getSessionE164();
|
||||||
if (sessionE164 != null && viewModel.getSessionId() != null) {
|
if (sessionE164 != null && viewModel.getSessionId() != null && viewModel.getCaptchaToken() == null) {
|
||||||
checkIfSessionIsInProgressAndAdvance(sessionE164);
|
checkIfSessionIsInProgressAndAdvance(sessionE164);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIfSessionIsInProgressAndAdvance(@NonNull String sessionE164) {
|
private void checkIfSessionIsInProgressAndAdvance(@NonNull String sessionE164) {
|
||||||
NavController navController = NavHostFragment.findNavController(this);
|
NavController navController = NavHostFragment.findNavController(this);
|
||||||
MccMncProducer mccMncProducer = new MccMncProducer(requireContext());
|
Disposable request = viewModel.validateSession(sessionE164)
|
||||||
Disposable request = viewModel.validateSession(sessionE164, mccMncProducer.getMcc(), mccMncProducer.getMnc())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(processor -> {
|
.subscribe(processor -> {
|
||||||
if (processor.hasResult() && processor.canSubmitProofImmediately()) {
|
if (processor.hasResult() && processor.canSubmitProofImmediately()) {
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.registration.fragments;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import androidx.navigation.ActivityNavigator;
|
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.LoggingFragment;
|
|
||||||
import org.thoughtcrime.securesms.MainActivity;
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
|
||||||
import org.thoughtcrime.securesms.pin.PinRestoreActivity;
|
|
||||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
|
||||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
||||||
import org.thoughtcrime.securesms.registration.RegistrationUtil;
|
|
||||||
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public final class RegistrationCompleteFragment extends LoggingFragment {
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(RegistrationCompleteFragment.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.fragment_registration_blank, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
|
|
||||||
FragmentActivity activity = requireActivity();
|
|
||||||
RegistrationViewModel viewModel = new ViewModelProvider(activity).get(RegistrationViewModel.class);
|
|
||||||
|
|
||||||
if (SignalStore.storageService().needsAccountRestore()) {
|
|
||||||
Log.i(TAG, "Performing pin restore");
|
|
||||||
activity.startActivity(new Intent(activity, PinRestoreActivity.class));
|
|
||||||
} else if (!viewModel.isReregister()) {
|
|
||||||
boolean needsProfile = Recipient.self().getProfileName().isEmpty() || !AvatarHelper.hasAvatar(activity, Recipient.self().getId());
|
|
||||||
boolean needsPin = !SignalStore.kbsValues().hasPin();
|
|
||||||
|
|
||||||
Log.i(TAG, "Pin restore flow not required." +
|
|
||||||
" profile name: " + Recipient.self().getProfileName().isEmpty() +
|
|
||||||
" profile avatar: " + !AvatarHelper.hasAvatar(activity, Recipient.self().getId()) +
|
|
||||||
" needsPin:" + needsPin);
|
|
||||||
|
|
||||||
Intent startIntent = MainActivity.clearTop(activity);
|
|
||||||
|
|
||||||
if (needsPin) {
|
|
||||||
startIntent = chainIntents(CreateKbsPinActivity.getIntentForPinCreate(requireContext()), startIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsProfile) {
|
|
||||||
startIntent = chainIntents(EditProfileActivity.getIntentForUserProfile(activity), startIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needsProfile && !needsPin) {
|
|
||||||
ApplicationDependencies.getJobManager()
|
|
||||||
.startChain(new ProfileUploadJob())
|
|
||||||
.then(Arrays.asList(new MultiDeviceProfileKeyUpdateJob(), new MultiDeviceProfileContentUpdateJob()))
|
|
||||||
.enqueue();
|
|
||||||
|
|
||||||
RegistrationUtil.maybeMarkRegistrationComplete(requireContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
activity.startActivity(startIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
activity.finish();
|
|
||||||
ActivityNavigator.applyPopAnimationsToPendingTransition(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @NonNull Intent chainIntents(@NonNull Intent sourceIntent, @NonNull Intent nextIntent) {
|
|
||||||
sourceIntent.putExtra("next_intent", nextIntent);
|
|
||||||
return sourceIntent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package org.thoughtcrime.securesms.registration.fragments
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.navigation.ActivityNavigator
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.LoggingFragment
|
||||||
|
import org.thoughtcrime.securesms.MainActivity
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob
|
||||||
|
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob
|
||||||
|
import org.thoughtcrime.securesms.jobs.ProfileUploadJob
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity
|
||||||
|
import org.thoughtcrime.securesms.pin.PinRestoreActivity
|
||||||
|
import org.thoughtcrime.securesms.profiles.AvatarHelper
|
||||||
|
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.registration.RegistrationUtil
|
||||||
|
import org.thoughtcrime.securesms.registration.viewmodel.RegistrationViewModel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [RegistrationCompleteFragment] is not visible to the user, but functions as basically a redirect towards one of:
|
||||||
|
* - [PIN Restore flow activity](org.thoughtcrime.securesms.pin.PinRestoreActivity)
|
||||||
|
* - [Profile](org.thoughtcrime.securesms.profiles.edit.EditProfileActivity) / [PIN creation](org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity) flow activities (this class chains the necessary activities together as an intent)
|
||||||
|
* - Exit registration flow and progress to conversation list
|
||||||
|
*/
|
||||||
|
class RegistrationCompleteFragment : LoggingFragment() {
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_registration_blank, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
val activity = requireActivity()
|
||||||
|
if (SignalStore.storageService().needsAccountRestore()) {
|
||||||
|
Log.i(TAG, "Performing pin restore.")
|
||||||
|
activity.startActivity(Intent(activity, PinRestoreActivity::class.java))
|
||||||
|
} else {
|
||||||
|
val viewModel: RegistrationViewModel by viewModels(ownerProducer = { requireActivity() })
|
||||||
|
|
||||||
|
val isProfileNameEmpty = Recipient.self().profileName.isEmpty
|
||||||
|
val isAvatarEmpty = !AvatarHelper.hasAvatar(activity, Recipient.self().id)
|
||||||
|
val needsProfile = isProfileNameEmpty || isAvatarEmpty
|
||||||
|
val needsPin = !SignalStore.kbsValues().hasPin() && !viewModel.isReregister
|
||||||
|
|
||||||
|
Log.i(TAG, "Pin restore flow not required. Profile name: $isProfileNameEmpty | Profile avatar: $isAvatarEmpty | Needs PIN: $needsPin")
|
||||||
|
|
||||||
|
if (!needsProfile && !needsPin) {
|
||||||
|
ApplicationDependencies.getJobManager()
|
||||||
|
.startChain(ProfileUploadJob())
|
||||||
|
.then(listOf(MultiDeviceProfileKeyUpdateJob(), MultiDeviceProfileContentUpdateJob()))
|
||||||
|
.enqueue()
|
||||||
|
RegistrationUtil.maybeMarkRegistrationComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
var startIntent = MainActivity.clearTop(activity)
|
||||||
|
|
||||||
|
if (needsPin) {
|
||||||
|
startIntent = chainIntents(CreateKbsPinActivity.getIntentForPinCreate(activity), startIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsProfile) {
|
||||||
|
startIntent = chainIntents(EditProfileActivity.getIntentForUserProfile(activity), startIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.startActivity(startIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.finish()
|
||||||
|
ActivityNavigator.applyPopAnimationsToPendingTransition(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun chainIntents(sourceIntent: Intent, nextIntent: Intent): Intent {
|
||||||
|
sourceIntent.putExtra("next_intent", nextIntent)
|
||||||
|
return sourceIntent
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = Log.tag(RegistrationCompleteFragment::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,8 @@ import org.thoughtcrime.securesms.registration.VerifyResponseWithSuccessfulKbs;
|
|||||||
import org.thoughtcrime.securesms.registration.VerifyResponseWithoutKbs;
|
import org.thoughtcrime.securesms.registration.VerifyResponseWithoutKbs;
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
@@ -43,6 +45,7 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||||||
private static final String STATE_REGISTRATION_SECRET = "REGISTRATION_SECRET";
|
private static final String STATE_REGISTRATION_SECRET = "REGISTRATION_SECRET";
|
||||||
private static final String STATE_VERIFICATION_CODE = "TEXT_CODE_ENTERED";
|
private static final String STATE_VERIFICATION_CODE = "TEXT_CODE_ENTERED";
|
||||||
private static final String STATE_CAPTCHA = "CAPTCHA";
|
private static final String STATE_CAPTCHA = "CAPTCHA";
|
||||||
|
private static final String STATE_PUSH_TIMED_OUT = "PUSH_TIMED_OUT";
|
||||||
private static final String STATE_INCORRECT_CODE_ATTEMPTS = "STATE_INCORRECT_CODE_ATTEMPTS";
|
private static final String STATE_INCORRECT_CODE_ATTEMPTS = "STATE_INCORRECT_CODE_ATTEMPTS";
|
||||||
private static final String STATE_REQUEST_RATE_LIMITER = "REQUEST_RATE_LIMITER";
|
private static final String STATE_REQUEST_RATE_LIMITER = "REQUEST_RATE_LIMITER";
|
||||||
private static final String STATE_KBS_TOKEN = "KBS_TOKEN";
|
private static final String STATE_KBS_TOKEN = "KBS_TOKEN";
|
||||||
@@ -71,6 +74,7 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||||||
setInitialDefaultValue(STATE_INCORRECT_CODE_ATTEMPTS, 0);
|
setInitialDefaultValue(STATE_INCORRECT_CODE_ATTEMPTS, 0);
|
||||||
setInitialDefaultValue(STATE_REQUEST_RATE_LIMITER, new LocalCodeRequestRateLimiter(60_000));
|
setInitialDefaultValue(STATE_REQUEST_RATE_LIMITER, new LocalCodeRequestRateLimiter(60_000));
|
||||||
setInitialDefaultValue(STATE_RECOVERY_PASSWORD, SignalStore.kbsValues().getRecoveryPassword());
|
setInitialDefaultValue(STATE_RECOVERY_PASSWORD, SignalStore.kbsValues().getRecoveryPassword());
|
||||||
|
setInitialDefaultValue(STATE_PUSH_TIMED_OUT, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> void setInitialDefaultValue(@NonNull String key, @Nullable T initialValue) {
|
protected <T> void setInitialDefaultValue(@NonNull String key, @Nullable T initialValue) {
|
||||||
@@ -172,6 +176,18 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||||||
return savedState.getLiveData(STATE_INCORRECT_CODE_ATTEMPTS, 0);
|
return savedState.getLiveData(STATE_INCORRECT_CODE_ATTEMPTS, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markPushChallengeTimedOut() {
|
||||||
|
savedState.set(STATE_PUSH_TIMED_OUT, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getExcludedChallenges() {
|
||||||
|
ArrayList<String> challengeKeys = new ArrayList<>();
|
||||||
|
if (Boolean.TRUE.equals(savedState.get(STATE_PUSH_TIMED_OUT))) {
|
||||||
|
challengeKeys.add(RegistrationSessionProcessor.PUSH_CHALLENGE_KEY);
|
||||||
|
}
|
||||||
|
return challengeKeys;
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable TokenData getKeyBackupCurrentToken() {
|
public @Nullable TokenData getKeyBackupCurrentToken() {
|
||||||
return savedState.get(STATE_KBS_TOKEN);
|
return savedState.get(STATE_KBS_TOKEN);
|
||||||
}
|
}
|
||||||
@@ -254,7 +270,7 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<RegistrationSessionProcessor.RegistrationSessionProcessorForSession> validateSession(String e164, @Nullable String mcc, @Nullable String mnc) {
|
public Single<RegistrationSessionProcessor.RegistrationSessionProcessorForSession> validateSession(String e164) {
|
||||||
String storedSessionId = null;
|
String storedSessionId = null;
|
||||||
if (e164.equals(getSessionE164())) {
|
if (e164.equals(getSessionE164())) {
|
||||||
storedSessionId = getSessionId();
|
storedSessionId = getSessionId();
|
||||||
@@ -264,11 +280,16 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Single<RegistrationSessionProcessor.RegistrationSessionProcessorForSession> getValidSession(String e164, @Nullable String mcc, @Nullable String mnc) {
|
public Single<RegistrationSessionProcessor.RegistrationSessionProcessorForSession> getValidSession(String e164, @Nullable String mcc, @Nullable String mnc) {
|
||||||
return validateSession(e164, mcc, mnc)
|
return validateSession(e164)
|
||||||
.flatMap(processor -> {
|
.flatMap(processor -> {
|
||||||
if (processor.isInvalidSession()) {
|
if (processor.isInvalidSession()) {
|
||||||
return verifyAccountRepository.requestValidSession(e164, getRegistrationSecret(), mcc, mnc)
|
return verifyAccountRepository.requestValidSession(e164, getRegistrationSecret(), mcc, mnc)
|
||||||
.map(RegistrationSessionProcessor.RegistrationSessionProcessorForSession::new);
|
.map(RegistrationSessionProcessor.RegistrationSessionProcessorForSession::new)
|
||||||
|
.doOnSuccess(createSessionProcessor -> {
|
||||||
|
if (createSessionProcessor.pushChallengeTimedOut()) {
|
||||||
|
markPushChallengeTimedOut();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return Single.just(processor);
|
return Single.just(processor);
|
||||||
}
|
}
|
||||||
@@ -289,7 +310,7 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
|||||||
return verifyAccountRepository.verifyCaptcha(sessionId, captcha, e164, getRegistrationSecret())
|
return verifyAccountRepository.verifyCaptcha(sessionId, captcha, e164, getRegistrationSecret())
|
||||||
.map(RegistrationSessionProcessor.RegistrationSessionProcessorForSession::new);
|
.map(RegistrationSessionProcessor.RegistrationSessionProcessorForSession::new);
|
||||||
} else {
|
} else {
|
||||||
String challenge = processor.getChallenge();
|
String challenge = processor.getChallenge(getExcludedChallenges());
|
||||||
Log.d(TAG, "Handling challenge of type " + challenge);
|
Log.d(TAG, "Handling challenge of type " + challenge);
|
||||||
if (challenge != null) {
|
if (challenge != null) {
|
||||||
switch (challenge) {
|
switch (challenge) {
|
||||||
|
|||||||
@@ -35,10 +35,14 @@ import org.whispersystems.signalservice.api.push.exceptions.IncorrectCodeExcepti
|
|||||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||||
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
|
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse;
|
||||||
|
import org.whispersystems.util.Base64;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.rxjava3.core.Single;
|
import io.reactivex.rxjava3.core.Single;
|
||||||
@@ -87,7 +91,11 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getFcmToken() {
|
public @Nullable String getFcmToken() {
|
||||||
return savedState.get(STATE_FCM_TOKEN);
|
String token = savedState.get(STATE_FCM_TOKEN);
|
||||||
|
if (token == null || token.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
@@ -350,7 +358,24 @@ public final class RegistrationViewModel extends BaseRegistrationViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Single<Boolean> checkForValidKbsAuthCredentials() {
|
private Single<Boolean> checkForValidKbsAuthCredentials() {
|
||||||
return registrationRepository.getKbsAuthCredential(getRegistrationData())
|
final List<String> kbsAuthTokenList = SignalStore.kbsValues().getKbsAuthTokenList();
|
||||||
|
List<String> usernamePasswords = kbsAuthTokenList
|
||||||
|
.stream()
|
||||||
|
.limit(10)
|
||||||
|
.map(t -> {
|
||||||
|
try {
|
||||||
|
return new String(Base64.decode(t.replace("Basic ", "").trim()), StandardCharsets.ISO_8859_1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (usernamePasswords.isEmpty()) {
|
||||||
|
return Single.just(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return registrationRepository.getKbsAuthCredential(getRegistrationData(), usernamePasswords)
|
||||||
.flatMap(p -> {
|
.flatMap(p -> {
|
||||||
if (p.getValid() != null) {
|
if (p.getValid() != null) {
|
||||||
return kbsRepository.getToken(p.getValid())
|
return kbsRepository.getToken(p.getValid())
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.signal.core.util.logging.Log;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|
||||||
import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob;
|
|
||||||
import org.thoughtcrime.securesms.jobs.RetrieveRemoteAnnouncementsJob;
|
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class VersionTracker {
|
|
||||||
|
|
||||||
private static final String TAG = Log.tag(VersionTracker.class);
|
|
||||||
|
|
||||||
public static int getLastSeenVersion(@NonNull Context context) {
|
|
||||||
return TextSecurePreferences.getLastVersionCode(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void updateLastSeenVersion(@NonNull Context context) {
|
|
||||||
int currentVersionCode = Util.getCanonicalVersionCode();
|
|
||||||
int lastVersionCode = TextSecurePreferences.getLastVersionCode(context);
|
|
||||||
|
|
||||||
if (currentVersionCode != lastVersionCode) {
|
|
||||||
Log.i(TAG, "Upgraded from " + lastVersionCode + " to " + currentVersionCode);
|
|
||||||
SignalStore.misc().clearClientDeprecated();
|
|
||||||
ApplicationDependencies.getJobManager().add(new RemoteConfigRefreshJob());
|
|
||||||
RetrieveRemoteAnnouncementsJob.enqueue(true);
|
|
||||||
LocalMetrics.getInstance().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
TextSecurePreferences.setLastVersionCode(context, currentVersionCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getDaysSinceFirstInstalled(Context context) {
|
|
||||||
try {
|
|
||||||
long installTimestamp = context.getPackageManager()
|
|
||||||
.getPackageInfo(context.getPackageName(), 0)
|
|
||||||
.firstInstallTime;
|
|
||||||
|
|
||||||
return TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - installTimestamp);
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package org.thoughtcrime.securesms.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
|
||||||
|
import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob
|
||||||
|
import org.thoughtcrime.securesms.jobs.RetrieveRemoteAnnouncementsJob
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
object VersionTracker {
|
||||||
|
private val TAG = Log.tag(VersionTracker::class.java)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getLastSeenVersion(context: Context): Int {
|
||||||
|
return TextSecurePreferences.getLastVersionCode(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun updateLastSeenVersion(context: Context) {
|
||||||
|
val currentVersionCode = Util.getCanonicalVersionCode()
|
||||||
|
val lastVersionCode = TextSecurePreferences.getLastVersionCode(context)
|
||||||
|
|
||||||
|
if (currentVersionCode != lastVersionCode) {
|
||||||
|
Log.i(TAG, "Upgraded from $lastVersionCode to $currentVersionCode")
|
||||||
|
SignalStore.misc().clearClientDeprecated()
|
||||||
|
val jobChain = listOf(RemoteConfigRefreshJob(), RefreshAttributesJob())
|
||||||
|
ApplicationDependencies.getJobManager().startChain(jobChain).enqueue()
|
||||||
|
RetrieveRemoteAnnouncementsJob.enqueue(true)
|
||||||
|
LocalMetrics.getInstance().clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
TextSecurePreferences.setLastVersionCode(context, currentVersionCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getDaysSinceFirstInstalled(context: Context): Long {
|
||||||
|
return try {
|
||||||
|
val installTimestamp = context.packageManager.getPackageInfo(context.packageName, 0).firstInstallTime
|
||||||
|
Duration.ofMillis(System.currentTimeMillis() - installTimestamp).toDays()
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -211,18 +211,7 @@ public class SignalServiceAccountManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Single<ServiceResponse<BackupAuthCheckResponse>> checkBackupAuthCredentials(@Nonnull String e164, @Nonnull List<String> basicAuthTokens) {
|
public Single<ServiceResponse<BackupAuthCheckResponse>> checkBackupAuthCredentials(@Nonnull String e164, @Nonnull List<String> usernamePasswords) {
|
||||||
List<String> usernamePasswords = basicAuthTokens
|
|
||||||
.stream()
|
|
||||||
.limit(10)
|
|
||||||
.map(t -> {
|
|
||||||
try {
|
|
||||||
return new String(Base64.decode(t.replace("Basic ", "").trim()), StandardCharsets.ISO_8859_1);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return pushServiceSocket.checkBackupAuthCredentials(new BackupAuthCheckRequest(e164, usernamePasswords), DefaultResponseMapper.getDefault(BackupAuthCheckResponse.class));
|
return pushServiceSocket.checkBackupAuthCredentials(new BackupAuthCheckRequest(e164, usernamePasswords), DefaultResponseMapper.getDefault(BackupAuthCheckResponse.class));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2625,7 +2625,7 @@ public class PushServiceSocket {
|
|||||||
RegistrationSessionMetadataHeaders responseHeaders = new RegistrationSessionMetadataHeaders(serverDeliveredTimestamp);
|
RegistrationSessionMetadataHeaders responseHeaders = new RegistrationSessionMetadataHeaders(serverDeliveredTimestamp);
|
||||||
RegistrationSessionMetadataJson responseBody = JsonUtil.fromJson(readBodyString(response), RegistrationSessionMetadataJson.class);
|
RegistrationSessionMetadataJson responseBody = JsonUtil.fromJson(readBodyString(response), RegistrationSessionMetadataJson.class);
|
||||||
|
|
||||||
return new RegistrationSessionMetadataResponse(responseHeaders, responseBody);
|
return new RegistrationSessionMetadataResponse(responseHeaders, responseBody, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class GroupHistory {
|
public static final class GroupHistory {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import com.fasterxml.jackson.annotation.JsonProperty
|
|||||||
*/
|
*/
|
||||||
data class RegistrationSessionMetadataResponse(
|
data class RegistrationSessionMetadataResponse(
|
||||||
val headers: RegistrationSessionMetadataHeaders,
|
val headers: RegistrationSessionMetadataHeaders,
|
||||||
val body: RegistrationSessionMetadataJson
|
val body: RegistrationSessionMetadataJson,
|
||||||
|
val state: RegistrationSessionState?,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class RegistrationSessionMetadataHeaders(
|
data class RegistrationSessionMetadataHeaders(
|
||||||
@@ -32,3 +33,7 @@ data class RegistrationSessionMetadataJson(
|
|||||||
return requestedInformation.contains("captcha")
|
return requestedInformation.contains("captcha")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class RegistrationSessionState(
|
||||||
|
var pushChallengeTimedOut: Boolean,
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user