Add capability to request username creation during registration.

This commit is contained in:
Alex Hart
2022-09-08 13:02:56 -03:00
committed by Greyson Parrelli
parent 7e45fc4a3e
commit 977af2c2f3
14 changed files with 328 additions and 75 deletions

View File

@@ -20,17 +20,20 @@ import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceTransferActivity;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity;
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
import org.thoughtcrime.securesms.pin.PinRestoreActivity;
import org.thoughtcrime.securesms.profiles.edit.EditProfileActivity;
import org.thoughtcrime.securesms.profiles.username.AddAUsernameActivity;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.AppStartup;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.util.Locale;
@@ -52,6 +55,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
private static final int STATE_TRANSFER_ONGOING = 8;
private static final int STATE_TRANSFER_LOCKED = 9;
private static final int STATE_CHANGE_NUMBER_LOCK = 10;
private static final int STATE_CREATE_USERNAME = 11;
private SignalServiceNetworkAccess networkAccess;
private BroadcastReceiver clearKeyReceiver;
@@ -156,6 +160,7 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
case STATE_TRANSFER_ONGOING: return getOldDeviceTransferIntent();
case STATE_TRANSFER_LOCKED: return getOldDeviceTransferLockedIntent();
case STATE_CHANGE_NUMBER_LOCK: return getChangeNumberLockIntent();
case STATE_CREATE_USERNAME: return getCreateUsernameIntent();
default: return null;
}
}
@@ -175,6 +180,8 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
return STATE_CREATE_SIGNAL_PIN;
} else if (userMustSetProfileName()) {
return STATE_CREATE_PROFILE_NAME;
} else if (shouldAskUserToCreateUsername()) {
return STATE_CREATE_USERNAME;
} else if (userMustCreateSignalPin()) {
return STATE_CREATE_SIGNAL_PIN;
} else if (EventBus.getDefault().getStickyEvent(TransferStatus.class) != null && getClass() != OldDeviceTransferActivity.class) {
@@ -200,6 +207,13 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
return !SignalStore.registrationValues().isRegistrationComplete() && Recipient.self().getProfileName().isEmpty();
}
private boolean shouldAskUserToCreateUsername() {
return FeatureFlags.usernames() &&
FeatureFlags.phoneNumberPrivacy() &&
!SignalStore.uiHints().hasSetOrSkippedUsernameCreation() &&
SignalStore.phoneNumberPrivacy().getPhoneNumberListingMode() == PhoneNumberPrivacyValues.PhoneNumberListingMode.UNLISTED;
}
private Intent getCreatePassphraseIntent() {
return getRoutedIntent(PassphraseCreateActivity.class, getIntent());
}
@@ -259,6 +273,10 @@ public abstract class PassphraseRequiredActivity extends BaseActivity implements
return ChangeNumberLockActivity.createIntent(this);
}
private Intent getCreateUsernameIntent() {
return getRoutedIntent(AddAUsernameActivity.class, getIntent());
}
private Intent getRoutedIntent(Intent destination, @Nullable Intent nextIntent) {
if (nextIntent != null) destination.putExtra("next_intent", nextIntent);
return destination;

View File

@@ -0,0 +1,22 @@
package org.thoughtcrime.securesms.components
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.card.MaterialCardView
import org.thoughtcrime.securesms.R
/**
* A small card with a circular progress indicator in it. Usable in place
* of a ProgressDialog, which is deprecated.
*
* Remember to add this as the last UI element in your XML hierarchy so it'll
* draw over top of other elements.
*/
class ProgressCard @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : MaterialCardView(context, attrs) {
init {
inflate(context, R.layout.progress_card, this)
}
}

View File

@@ -9,6 +9,7 @@ public class UiHints extends SignalStoreValues {
private static final String HAS_SEEN_GROUP_SETTINGS_MENU_TOAST = "uihints.has_seen_group_settings_menu_toast";
private static final String HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE = "uihints.has_confirmed_delete_for_everyone_once";
private static final String HAS_SET_OR_SKIPPED_USERNAME_CREATION = "uihints.has_set_or_skipped_username_creation";
UiHints(@NonNull KeyValueStore store) {
super(store);
@@ -39,4 +40,12 @@ public class UiHints extends SignalStoreValues {
public boolean hasConfirmedDeleteForEveryoneOnce() {
return getBoolean(HAS_CONFIRMED_DELETE_FOR_EVERYONE_ONCE, false);
}
public boolean hasSetOrSkippedUsernameCreation() {
return getBoolean(HAS_SET_OR_SKIPPED_USERNAME_CREATION, false);
}
public void markHasSetOrSkippedUsernameCreation() {
putBoolean(HAS_SET_OR_SKIPPED_USERNAME_CREATION, true);
}
}

View File

@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.profiles.edit;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -20,7 +19,6 @@ import org.thoughtcrime.securesms.util.DynamicTheme;
/**
* Shows editing screen for your profile during registration. Also handles group name editing.
*/
@SuppressLint("StaticFieldLeak")
public class EditProfileActivity extends BaseActivity implements EditProfileFragment.Controller {
public static final String NEXT_INTENT = "next_intent";
@@ -37,13 +35,6 @@ public class EditProfileActivity extends BaseActivity implements EditProfileFrag
return intent;
}
public static @NonNull Intent getIntentForUserProfileEdit(@NonNull Context context) {
Intent intent = new Intent(context, EditProfileActivity.class);
intent.putExtra(EditProfileActivity.EXCLUDE_SYSTEM, true);
intent.putExtra(EditProfileActivity.NEXT_BUTTON_TEXT, R.string.save);
return intent;
}
public static @NonNull Intent getIntentForGroupProfile(@NonNull Context context, @NonNull GroupId groupId) {
Intent intent = new Intent(context, EditProfileActivity.class);
intent.putExtra(EditProfileActivity.SHOW_TOOLBAR, true);

View File

@@ -18,6 +18,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.navigation.Navigation;
@@ -91,7 +92,7 @@ public class EditProfileFragment extends LoggingFragment {
GroupId groupId = GroupId.parseNullableOrThrow(requireArguments().getString(GROUP_ID, null));
initializeViewModel(requireArguments().getBoolean(EXCLUDE_SYSTEM, false), groupId, savedInstanceState != null);
initializeResources(view, groupId);
initializeResources(groupId);
initializeProfileAvatar();
initializeProfileName();
@@ -151,11 +152,10 @@ public class EditProfileFragment extends LoggingFragment {
EditProfileViewModel.Factory factory = new EditProfileViewModel.Factory(repository, hasSavedInstanceState, groupId);
viewModel = ViewModelProviders.of(requireActivity(), factory)
.get(EditProfileViewModel.class);
viewModel = new ViewModelProvider(requireActivity(), factory).get(EditProfileViewModel.class);
}
private void initializeResources(@NonNull View view, @Nullable GroupId groupId) {
private void initializeResources(@Nullable GroupId groupId) {
Bundle arguments = requireArguments();
boolean isEditingGroup = groupId != null;

View File

@@ -1,7 +1,7 @@
package org.thoughtcrime.securesms.profiles.manage;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -17,6 +17,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;
@@ -29,10 +30,12 @@ import com.google.android.material.textfield.TextInputLayout;
import org.signal.core.util.DimensionUnit;
import org.thoughtcrime.securesms.LoggingFragment;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contactshare.SimpleTextWatcher;
import org.thoughtcrime.securesms.databinding.UsernameEditFragmentBinding;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.AccessibilityUtil;
import org.thoughtcrime.securesms.util.FragmentResultContract;
import org.thoughtcrime.securesms.util.LifecycleDisposable;
import org.thoughtcrime.securesms.util.UsernameUtil;
@@ -49,6 +52,7 @@ public class UsernameEditFragment extends LoggingFragment {
private UsernameEditFragmentBinding binding;
private ImageView suffixProgress;
private LifecycleDisposable lifecycleDisposable;
private UsernameEditFragmentArgs args;
public static UsernameEditFragment newInstance() {
return new UsernameEditFragment();
@@ -62,20 +66,37 @@ public class UsernameEditFragment extends LoggingFragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
binding.toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).popBackStack());
Bundle bundle = getArguments();
if (bundle != null) {
args = UsernameEditFragmentArgs.fromBundle(bundle);
} else {
args = new UsernameEditFragmentArgs.Builder().build();
}
if (args.getIsInRegistration()) {
binding.toolbar.setNavigationIcon(null);
binding.toolbar.setTitle(R.string.UsernameEditFragment__add_a_username);
binding.usernameSkipButton.setVisibility(View.VISIBLE);
binding.usernameDoneButton.setVisibility(View.VISIBLE);
} else {
binding.toolbar.setNavigationOnClickListener(v -> Navigation.findNavController(view).popBackStack());
binding.usernameSubmitButton.setVisibility(View.VISIBLE);
}
binding.usernameTextWrapper.setErrorIconDrawable(null);
lifecycleDisposable = new LifecycleDisposable();
lifecycleDisposable.bindTo(getViewLifecycleOwner());
viewModel = new ViewModelProvider(this, new UsernameEditViewModel.Factory()).get(UsernameEditViewModel.class);
viewModel = new ViewModelProvider(this, new UsernameEditViewModel.Factory(args.getIsInRegistration())).get(UsernameEditViewModel.class);
lifecycleDisposable.add(viewModel.getUiState().subscribe(this::onUiStateChanged));
viewModel.getEvents().observe(getViewLifecycleOwner(), this::onEvent);
binding.usernameSubmitButton.setOnClickListener(v -> viewModel.onUsernameSubmitted());
binding.usernameDeleteButton.setOnClickListener(v -> viewModel.onUsernameDeleted());
binding.usernameDoneButton.setOnClickListener(v -> viewModel.onUsernameSubmitted());
binding.usernameSkipButton.setOnClickListener(v -> viewModel.onUsernameSkipped());
UsernameState usernameState = Recipient.self().getUsername().<UsernameState>map(UsernameState.Set::new).orElse(UsernameState.NoUsername.INSTANCE);
binding.usernameText.setText(usernameState.getNickname());
@@ -142,15 +163,82 @@ public class UsernameEditFragment extends LoggingFragment {
}
private void onUiStateChanged(@NonNull UsernameEditViewModel.State state) {
EditText usernameInput = binding.usernameText;
TextInputLayout usernameInputWrapper = binding.usernameTextWrapper;
presentSuffix(state.getUsername());
presentButtonState(state.getButtonState());
switch (state.getUsernameStatus()) {
case NONE:
usernameInputWrapper.setError(null);
break;
case TOO_SHORT:
case TOO_LONG:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_LENGTH, UsernameUtil.MAX_LENGTH));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case INVALID_CHARACTERS:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_can_only_include));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case CANNOT_START_WITH_NUMBER:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case INVALID_GENERIC:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_username_is_invalid));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case TAKEN:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_taken));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
}
}
private void presentButtonState(@NonNull UsernameEditViewModel.ButtonState buttonState) {
if (args.getIsInRegistration()) {
presentRegistrationButtonState(buttonState);
} else {
presentProfileUpdateButtonState(buttonState);
}
}
private void presentRegistrationButtonState(@NonNull UsernameEditViewModel.ButtonState buttonState) {
binding.usernameText.setEnabled(true);
binding.usernameProgressCard.setVisibility(View.GONE);
switch (buttonState) {
case SUBMIT:
binding.usernameDoneButton.setEnabled(true);
binding.usernameDoneButton.setAlpha(1f);
break;
case SUBMIT_DISABLED:
binding.usernameDoneButton.setEnabled(false);
binding.usernameDoneButton.setAlpha(DISABLED_ALPHA);
break;
case SUBMIT_LOADING:
binding.usernameDoneButton.setEnabled(false);
binding.usernameDoneButton.setAlpha(DISABLED_ALPHA);
binding.usernameProgressCard.setVisibility(View.VISIBLE);
break;
default:
throw new IllegalStateException("Delete functionality is not available during registration.");
}
}
private void presentProfileUpdateButtonState(@NonNull UsernameEditViewModel.ButtonState buttonState) {
CircularProgressMaterialButton submitButton = binding.usernameSubmitButton;
CircularProgressMaterialButton deleteButton = binding.usernameDeleteButton;
TextInputLayout usernameInputWrapper = binding.usernameTextWrapper;
EditText usernameInput = binding.usernameText;
usernameInput.setEnabled(true);
presentSuffix(state.getUsername());
switch (state.getButtonState()) {
switch (buttonState) {
case SUBMIT:
submitButton.cancelSpinning();
submitButton.setVisibility(View.VISIBLE);
@@ -194,38 +282,6 @@ public class UsernameEditFragment extends LoggingFragment {
usernameInput.setEnabled(false);
break;
}
switch (state.getUsernameStatus()) {
case NONE:
usernameInputWrapper.setError(null);
break;
case TOO_SHORT:
case TOO_LONG:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_must_be_between_a_and_b_characters, UsernameUtil.MIN_LENGTH, UsernameUtil.MAX_LENGTH));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case INVALID_CHARACTERS:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_can_only_include));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case CANNOT_START_WITH_NUMBER:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_usernames_cannot_begin_with_a_number));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case INVALID_GENERIC:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_username_is_invalid));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
case TAKEN:
usernameInputWrapper.setError(getResources().getString(R.string.UsernameEditFragment_this_username_is_taken));
usernameInputWrapper.setErrorTextColor(ColorStateList.valueOf(getResources().getColor(R.color.signal_colorError)));
break;
}
}
private void presentSuffix(@NonNull UsernameState usernameState) {
@@ -257,7 +313,7 @@ public class UsernameEditFragment extends LoggingFragment {
switch (event) {
case SUBMIT_SUCCESS:
ResultContract.setUsernameCreated(getParentFragmentManager());
NavHostFragment.findNavController(this).popBackStack();
closeScreen();
break;
case SUBMIT_FAIL_TAKEN:
Toast.makeText(requireContext(), R.string.UsernameEditFragment_this_username_is_taken, Toast.LENGTH_SHORT).show();
@@ -272,6 +328,36 @@ public class UsernameEditFragment extends LoggingFragment {
case NETWORK_FAILURE:
Toast.makeText(requireContext(), R.string.UsernameEditFragment_encountered_a_network_error, Toast.LENGTH_SHORT).show();
break;
case SKIPPED:
closeScreen();
break;
}
}
private void closeScreen() {
if (args.getIsInRegistration()) {
finishAndStartNextIntent();
} else {
NavHostFragment.findNavController(this).popBackStack();
}
}
private void finishAndStartNextIntent() {
FragmentActivity activity = requireActivity();
boolean didLaunch = false;
Intent activityIntent = activity.getIntent();
if (activityIntent != null) {
Intent nextIntent = activityIntent.getParcelableExtra(PassphraseRequiredActivity.NEXT_INTENT_EXTRA);
if (nextIntent != null) {
activity.startActivity(nextIntent);
activity.finish();
didLaunch = true;
}
}
if (!didLaunch) {
activity.finish();
}
}

View File

@@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.signal.core.util.ThreadUtil;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.SingleLiveEvent;
import org.thoughtcrime.securesms.util.UsernameUtil;
@@ -45,13 +46,15 @@ class UsernameEditViewModel extends ViewModel {
private final RxStore<State> uiState;
private final PublishProcessor<String> nicknamePublisher;
private final CompositeDisposable disposables;
private final boolean isInRegistration;
private UsernameEditViewModel() {
private UsernameEditViewModel(boolean isInRegistration) {
this.repo = new UsernameEditRepository();
this.uiState = new RxStore<>(new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, Recipient.self().getUsername().<UsernameState>map(UsernameState.Set::new).orElse(UsernameState.NoUsername.INSTANCE)), Schedulers.computation());
this.events = new SingleLiveEvent<>();
this.nicknamePublisher = PublishProcessor.create();
this.disposables = new CompositeDisposable();
this.isInRegistration = isInRegistration;
Disposable disposable = nicknamePublisher.debounce(NICKNAME_PUBLISHER_DEBOUNCE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.subscribe(this::onNicknameChanged);
@@ -67,7 +70,7 @@ class UsernameEditViewModel extends ViewModel {
void onNicknameUpdated(@NonNull String nickname) {
uiState.update(state -> {
if (TextUtils.isEmpty(nickname) && Recipient.self().getUsername().isPresent()) {
return new State(ButtonState.DELETE, UsernameStatus.NONE, UsernameState.NoUsername.INSTANCE);
return new State(isInRegistration ? ButtonState.SUBMIT_DISABLED : ButtonState.DELETE, UsernameStatus.NONE, UsernameState.NoUsername.INSTANCE);
}
Optional<InvalidReason> invalidReason = UsernameUtil.checkUsername(nickname);
@@ -79,6 +82,11 @@ class UsernameEditViewModel extends ViewModel {
nicknamePublisher.onNext(nickname);
}
void onUsernameSkipped() {
SignalStore.uiHints().markHasSetOrSkippedUsernameCreation();
events.setValue(Event.SKIPPED);
}
void onUsernameSubmitted() {
UsernameState usernameState = uiState.getState().getUsername();
@@ -107,6 +115,7 @@ class UsernameEditViewModel extends ViewModel {
switch (result) {
case SUCCESS:
SignalStore.uiHints().markHasSetOrSkippedUsernameCreation();
uiState.update(state -> new State(ButtonState.SUBMIT_DISABLED, UsernameStatus.NONE, state.usernameState));
events.postValue(Event.SUBMIT_SUCCESS);
break;
@@ -248,14 +257,21 @@ class UsernameEditViewModel extends ViewModel {
}
enum Event {
NETWORK_FAILURE, SUBMIT_SUCCESS, DELETE_SUCCESS, SUBMIT_FAIL_INVALID, SUBMIT_FAIL_TAKEN
NETWORK_FAILURE, SUBMIT_SUCCESS, DELETE_SUCCESS, SUBMIT_FAIL_INVALID, SUBMIT_FAIL_TAKEN, SKIPPED
}
static class Factory extends ViewModelProvider.NewInstanceFactory {
private final boolean isInRegistration;
Factory(boolean isInRegistration) {
this.isInRegistration = isInRegistration;
}
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection ConstantConditions
return modelClass.cast(new UsernameEditViewModel());
return modelClass.cast(new UsernameEditViewModel(isInRegistration));
}
}
}

View File

@@ -0,0 +1,37 @@
package org.thoughtcrime.securesms.profiles.username
import android.os.Bundle
import androidx.navigation.fragment.NavHostFragment
import org.thoughtcrime.securesms.BaseActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.profiles.manage.UsernameEditFragmentArgs
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
import org.thoughtcrime.securesms.util.DynamicTheme
class AddAUsernameActivity : BaseActivity() {
protected open val dynamicTheme: DynamicTheme = DynamicNoActionBarTheme()
protected open val contentViewId: Int = R.layout.fragment_container
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(contentViewId)
dynamicTheme.onCreate(this)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(
R.id.fragment_container,
NavHostFragment.create(
R.navigation.create_username,
UsernameEditFragmentArgs.Builder().setIsInRegistration(true).build().toBundle()
)
)
.commit()
}
}
override fun onResume() {
super.onResume()
dynamicTheme.onResume(this)
}
}