mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
[android] do not show two private conversation onboardings at the same time
Checking for introduction onboarding is now done in the ViewModel together with the image onboarding. The latter has preference. If both could be shown, the introduction onboarding will be delayed to the next time the user enters the conversation.
This commit is contained in:
@@ -46,7 +46,6 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
@@ -126,7 +125,6 @@ import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHME
|
|||||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
||||||
@@ -141,11 +139,10 @@ public class ConversationActivity extends BriarActivity
|
|||||||
TextCache, AttachmentCache, AttachImageListener {
|
TextCache, AttachmentCache, AttachImageListener {
|
||||||
|
|
||||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||||
|
private static final int ONBOARDING_DELAY = 750;
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ConversationActivity.class.getName());
|
Logger.getLogger(ConversationActivity.class.getName());
|
||||||
private static final String SHOW_ONBOARDING_INTRODUCTION =
|
|
||||||
"showOnboardingIntroduction";
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AndroidNotificationManager notificationManager;
|
AndroidNotificationManager notificationManager;
|
||||||
@@ -355,10 +352,19 @@ public class ConversationActivity extends BriarActivity
|
|||||||
MenuInflater inflater = getMenuInflater();
|
MenuInflater inflater = getMenuInflater();
|
||||||
inflater.inflate(R.menu.conversation_actions, menu);
|
inflater.inflate(R.menu.conversation_actions, menu);
|
||||||
|
|
||||||
enableIntroductionActionIfAvailable(
|
// enable introduction action if available
|
||||||
menu.findItem(R.id.action_introduction));
|
observeOnce(viewModel.showIntroductionAction(), this, enable -> {
|
||||||
enableAliasActionIfAvailable(
|
if (enable != null && enable) {
|
||||||
menu.findItem(R.id.action_set_alias));
|
menu.findItem(R.id.action_introduction).setEnabled(true);
|
||||||
|
// show introduction onboarding, if needed
|
||||||
|
observeOnce(viewModel.showIntroductionOnboarding(), this,
|
||||||
|
this::showIntroductionOnboarding);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// enable alias action if available
|
||||||
|
observeOnce(viewModel.getContact(), this, contact -> {
|
||||||
|
menu.findItem(R.id.action_set_alias).setEnabled(true);
|
||||||
|
});
|
||||||
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
@@ -713,32 +719,6 @@ public class ConversationActivity extends BriarActivity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableIntroductionActionIfAvailable(MenuItem item) {
|
|
||||||
runOnDbThread(() -> {
|
|
||||||
try {
|
|
||||||
if (contactManager.getActiveContacts().size() > 1) {
|
|
||||||
enableIntroductionAction(item);
|
|
||||||
Settings settings =
|
|
||||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
|
||||||
if (settings.getBoolean(SHOW_ONBOARDING_INTRODUCTION,
|
|
||||||
true)) {
|
|
||||||
showIntroductionOnboarding();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableAliasActionIfAvailable(MenuItem item) {
|
|
||||||
observeOnce(viewModel.getContact(), this, c -> item.setEnabled(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableIntroductionAction(MenuItem item) {
|
|
||||||
runOnUiThreadUnlessDestroyed(() -> item.setEnabled(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showImageOnboarding(@Nullable Boolean show) {
|
private void showImageOnboarding(@Nullable Boolean show) {
|
||||||
if (show == null || !show) return;
|
if (show == null || !show) return;
|
||||||
// show onboarding only after the enter transition has ended
|
// show onboarding only after the enter transition has ended
|
||||||
@@ -747,12 +727,15 @@ public class ConversationActivity extends BriarActivity
|
|||||||
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
||||||
((TextAttachmentController) sendController)
|
((TextAttachmentController) sendController)
|
||||||
.showImageOnboarding(this, () ->
|
.showImageOnboarding(this, () ->
|
||||||
viewModel.imageOnboardingSeen());
|
viewModel.onImageOnboardingSeen());
|
||||||
}, 750);
|
}, ONBOARDING_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showIntroductionOnboarding() {
|
private void showIntroductionOnboarding(@Nullable Boolean show) {
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
if (show == null || !show) return;
|
||||||
|
// show onboarding only after the enter transition has ended
|
||||||
|
// otherwise the tap target animation won't play
|
||||||
|
textInputView.postDelayed(() -> {
|
||||||
// find view of overflow icon
|
// find view of overflow icon
|
||||||
View target = null;
|
View target = null;
|
||||||
for (int i = 0; i < toolbar.getChildCount(); i++) {
|
for (int i = 0; i < toolbar.getChildCount(); i++) {
|
||||||
@@ -770,7 +753,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
PromptStateChangeListener listener = (prompt, state) -> {
|
PromptStateChangeListener listener = (prompt, state) -> {
|
||||||
if (state == STATE_DISMISSED || state == STATE_FINISHED) {
|
if (state == STATE_DISMISSED || state == STATE_FINISHED) {
|
||||||
introductionOnboardingSeen();
|
viewModel.onIntroductionOnboardingSeen();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
new MaterialTapTargetPrompt.Builder(ConversationActivity.this,
|
new MaterialTapTargetPrompt.Builder(ConversationActivity.this,
|
||||||
@@ -782,19 +765,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
ContextCompat.getColor(this, R.color.briar_primary))
|
ContextCompat.getColor(this, R.color.briar_primary))
|
||||||
.setPromptStateChangeListener(listener)
|
.setPromptStateChangeListener(listener)
|
||||||
.show();
|
.show();
|
||||||
});
|
}, ONBOARDING_DELAY);
|
||||||
}
|
|
||||||
|
|
||||||
private void introductionOnboardingSeen() {
|
|
||||||
runOnDbThread(() -> {
|
|
||||||
try {
|
|
||||||
Settings settings = new Settings();
|
|
||||||
settings.putBoolean(SHOW_ONBOARDING_INTRODUCTION, false);
|
|
||||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -62,6 +63,8 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
getLogger(ConversationViewModel.class.getName());
|
getLogger(ConversationViewModel.class.getName());
|
||||||
private static final String SHOW_ONBOARDING_IMAGE =
|
private static final String SHOW_ONBOARDING_IMAGE =
|
||||||
"showOnboardingImage";
|
"showOnboardingImage";
|
||||||
|
private static final String SHOW_ONBOARDING_INTRODUCTION =
|
||||||
|
"showOnboardingIntroduction";
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
@@ -86,6 +89,10 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<Boolean> showImageOnboarding =
|
private final MutableLiveData<Boolean> showImageOnboarding =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> showIntroductionOnboarding =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
private final MutableLiveData<Boolean> showIntroductionAction =
|
||||||
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<Boolean> contactDeleted =
|
private final MutableLiveData<Boolean> contactDeleted =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<GroupId> messagingGroupId =
|
private final MutableLiveData<GroupId> messagingGroupId =
|
||||||
@@ -115,25 +122,28 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
contactDeleted.setValue(false);
|
contactDeleted.setValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting the {@link ContactId} automatically triggers loading of other
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
void setContactId(ContactId contactId) {
|
void setContactId(ContactId contactId) {
|
||||||
if (this.contactId == null) {
|
if (this.contactId == null) {
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
loadContact();
|
loadContact(contactId);
|
||||||
} else if (!contactId.equals(this.contactId)) {
|
} else if (!contactId.equals(this.contactId)) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadContact() {
|
private void loadContact(ContactId contactId) {
|
||||||
dbExecutor.execute(() -> {
|
dbExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
long start = now();
|
long start = now();
|
||||||
Contact c =
|
Contact c = contactManager.getContact(contactId);
|
||||||
contactManager.getContact(requireNonNull(contactId));
|
|
||||||
contact.postValue(c);
|
contact.postValue(c);
|
||||||
logDuration(LOG, "Loading contact", start);
|
logDuration(LOG, "Loading contact", start);
|
||||||
start = now();
|
start = now();
|
||||||
checkImageSupport(c.getId());
|
checkFeaturesAndOnboarding(contactId);
|
||||||
logDuration(LOG, "Checking for image support", start);
|
logDuration(LOG, "Checking for image support", start);
|
||||||
} catch (NoSuchContactException e) {
|
} catch (NoSuchContactException e) {
|
||||||
contactDeleted.postValue(true);
|
contactDeleted.postValue(true);
|
||||||
@@ -148,7 +158,7 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
try {
|
try {
|
||||||
contactManager.setContactAlias(requireNonNull(contactId),
|
contactManager.setContactAlias(requireNonNull(contactId),
|
||||||
alias.isEmpty() ? null : alias);
|
alias.isEmpty() ? null : alias);
|
||||||
loadContact();
|
loadContact(contactId);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
@@ -177,28 +187,54 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private void checkImageSupport(ContactId c) throws DbException {
|
private void checkFeaturesAndOnboarding(ContactId c) throws DbException {
|
||||||
|
// check if images are supported
|
||||||
int minorVersion = db.transactionWithResult(true, txn ->
|
int minorVersion = db.transactionWithResult(true, txn ->
|
||||||
clientVersioningManager
|
clientVersioningManager
|
||||||
.getClientMinorVersion(txn, c, CLIENT_ID, 0));
|
.getClientMinorVersion(txn, c, CLIENT_ID, 0));
|
||||||
// support was added in 0.1
|
// support was added in 0.1
|
||||||
boolean imagesSupported = minorVersion == 1;
|
boolean imagesSupported = minorVersion == 1;
|
||||||
imageSupport.postValue(imagesSupported);
|
imageSupport.postValue(imagesSupported);
|
||||||
if (!imagesSupported) return;
|
|
||||||
|
|
||||||
// check if we should show onboarding, only if images are supported
|
// check if introductions are supported
|
||||||
Settings settings =
|
Collection<Contact> contacts = contactManager.getActiveContacts();
|
||||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
boolean introductionSupported = contacts.size() > 1;
|
||||||
if (settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) {
|
showIntroductionAction.postValue(introductionSupported);
|
||||||
|
|
||||||
|
Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||||
|
if (imagesSupported &&
|
||||||
|
settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) {
|
||||||
|
// check if we should show onboarding, only if images are supported
|
||||||
showImageOnboarding.postValue(true);
|
showImageOnboarding.postValue(true);
|
||||||
|
// allow observer to stop listening for changes
|
||||||
|
showIntroductionOnboarding.postValue(false);
|
||||||
|
} else {
|
||||||
|
// allow observer to stop listening for changes
|
||||||
|
showImageOnboarding.postValue(false);
|
||||||
|
// we only show one onboarding dialog at a time
|
||||||
|
if (introductionSupported &&
|
||||||
|
settings.getBoolean(SHOW_ONBOARDING_INTRODUCTION, true)) {
|
||||||
|
showIntroductionOnboarding.postValue(true);
|
||||||
|
} else {
|
||||||
|
// allow observer to stop listening for changes
|
||||||
|
showIntroductionOnboarding.postValue(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void imageOnboardingSeen() {
|
void onImageOnboardingSeen() {
|
||||||
|
onOnboardingSeen(SHOW_ONBOARDING_IMAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onIntroductionOnboardingSeen() {
|
||||||
|
onOnboardingSeen(SHOW_ONBOARDING_INTRODUCTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onOnboardingSeen(String key) {
|
||||||
dbExecutor.execute(() -> {
|
dbExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
Settings settings = new Settings();
|
Settings settings = new Settings();
|
||||||
settings.putBoolean(SHOW_ONBOARDING_IMAGE, false);
|
settings.putBoolean(key, false);
|
||||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -322,6 +358,14 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
return showImageOnboarding;
|
return showImageOnboarding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> showIntroductionOnboarding() {
|
||||||
|
return showIntroductionOnboarding;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> showIntroductionAction() {
|
||||||
|
return showIntroductionAction;
|
||||||
|
}
|
||||||
|
|
||||||
LiveData<Boolean> isContactDeleted() {
|
LiveData<Boolean> isContactDeleted() {
|
||||||
return contactDeleted;
|
return contactDeleted;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user