mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
[android] only enable image feature if contact supports it
Also show an onboarding the first time, the feature gets activiated
This commit is contained in:
@@ -263,6 +263,13 @@ public class ConversationActivity extends BriarActivity
|
||||
ImagePreview imagePreview = findViewById(R.id.imagePreview);
|
||||
sendController = new TextAttachmentController(textInputView,
|
||||
imagePreview, this, this);
|
||||
observeOnce(viewModel.hasImageSupport(), this, hasSupport -> {
|
||||
if (hasSupport != null && hasSupport) {
|
||||
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
||||
((TextAttachmentController) sendController)
|
||||
.setImagesSupported();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sendController = new TextSendController(textInputView, this, false);
|
||||
}
|
||||
@@ -461,6 +468,10 @@ public class ConversationActivity extends BriarActivity
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
textInputView.setEnabled(true);
|
||||
// start observing onboarding after enabling (only once, because
|
||||
// we only update this when an onboarding should be shown)
|
||||
observeOnce(viewModel.showImageOnboarding(), this,
|
||||
this::showImageOnboarding);
|
||||
List<ConversationItem> items = createItems(headers);
|
||||
adapter.addAll(items);
|
||||
list.showData();
|
||||
@@ -728,6 +739,18 @@ public class ConversationActivity extends BriarActivity
|
||||
runOnUiThreadUnlessDestroyed(() -> item.setEnabled(true));
|
||||
}
|
||||
|
||||
private void showImageOnboarding(@Nullable Boolean show) {
|
||||
if (show == null || !show) return;
|
||||
// show onboarding only after the enter transition has ended
|
||||
// otherwise the tap target animation won't play
|
||||
textInputView.postDelayed(() -> {
|
||||
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
||||
((TextAttachmentController) sendController)
|
||||
.showImageOnboarding(this, () ->
|
||||
viewModel.imageOnboardingSeen());
|
||||
}, 750);
|
||||
}
|
||||
|
||||
private void showIntroductionOnboarding() {
|
||||
runOnUiThreadUnlessDestroyed(() -> {
|
||||
// find view of overflow icon
|
||||
@@ -755,6 +778,8 @@ public class ConversationActivity extends BriarActivity
|
||||
.setPrimaryText(R.string.introduction_onboarding_title)
|
||||
.setSecondaryText(R.string.introduction_onboarding_text)
|
||||
.setIcon(R.drawable.ic_more_vert_accent)
|
||||
.setBackgroundColour(
|
||||
ContextCompat.getColor(this, R.color.briar_primary))
|
||||
.setPromptStateChangeListener(listener)
|
||||
.show();
|
||||
});
|
||||
|
||||
@@ -16,13 +16,17 @@ import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
import org.briarproject.briar.api.messaging.Attachment;
|
||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||
@@ -47,21 +51,28 @@ import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
||||
import static org.briarproject.briar.api.messaging.MessagingManager.CLIENT_ID;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ConversationViewModel extends AndroidViewModel {
|
||||
|
||||
private static Logger LOG =
|
||||
getLogger(ConversationViewModel.class.getName());
|
||||
private static final String SHOW_ONBOARDING_IMAGE =
|
||||
"showOnboardingImage";
|
||||
|
||||
@DatabaseExecutor
|
||||
private final Executor dbExecutor;
|
||||
@CryptoExecutor
|
||||
private final Executor cryptoExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final MessagingManager messagingManager;
|
||||
private final ContactManager contactManager;
|
||||
private final SettingsManager settingsManager;
|
||||
private final PrivateMessageFactory privateMessageFactory;
|
||||
private final ClientVersioningManager clientVersioningManager;
|
||||
private final AttachmentController attachmentController;
|
||||
|
||||
@Nullable
|
||||
@@ -71,6 +82,10 @@ public class ConversationViewModel extends AndroidViewModel {
|
||||
Transformations.map(contact, c -> c.getAuthor().getId());
|
||||
private final LiveData<String> contactName =
|
||||
Transformations.map(contact, UiUtils::getContactDisplayName);
|
||||
private final MutableLiveData<Boolean> imageSupport =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> showImageOnboarding =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> contactDeleted =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<GroupId> messagingGroupId =
|
||||
@@ -81,16 +96,20 @@ public class ConversationViewModel extends AndroidViewModel {
|
||||
@Inject
|
||||
ConversationViewModel(Application application,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
@CryptoExecutor Executor cryptoExecutor,
|
||||
MessagingManager messagingManager,
|
||||
ContactManager contactManager,
|
||||
PrivateMessageFactory privateMessageFactory) {
|
||||
@CryptoExecutor Executor cryptoExecutor, DatabaseComponent db,
|
||||
MessagingManager messagingManager, ContactManager contactManager,
|
||||
SettingsManager settingsManager,
|
||||
PrivateMessageFactory privateMessageFactory,
|
||||
ClientVersioningManager clientVersioningManager) {
|
||||
super(application);
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.cryptoExecutor = cryptoExecutor;
|
||||
this.db = db;
|
||||
this.messagingManager = messagingManager;
|
||||
this.contactManager = contactManager;
|
||||
this.settingsManager = settingsManager;
|
||||
this.privateMessageFactory = privateMessageFactory;
|
||||
this.clientVersioningManager = clientVersioningManager;
|
||||
this.attachmentController = new AttachmentController(messagingManager,
|
||||
application.getResources());
|
||||
contactDeleted.setValue(false);
|
||||
@@ -113,6 +132,9 @@ public class ConversationViewModel extends AndroidViewModel {
|
||||
contactManager.getContact(requireNonNull(contactId));
|
||||
contact.postValue(c);
|
||||
logDuration(LOG, "Loading contact", start);
|
||||
start = now();
|
||||
checkImageSupport(c.getId());
|
||||
logDuration(LOG, "Checking for image support", start);
|
||||
} catch (NoSuchContactException e) {
|
||||
contactDeleted.postValue(true);
|
||||
} catch (DbException e) {
|
||||
@@ -154,6 +176,36 @@ public class ConversationViewModel extends AndroidViewModel {
|
||||
});
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void checkImageSupport(ContactId c) throws DbException {
|
||||
int minorVersion = db.transactionWithResult(true, txn ->
|
||||
clientVersioningManager
|
||||
.getClientMinorVersion(txn, c, CLIENT_ID, 0));
|
||||
// support was added in 0.1
|
||||
boolean imagesSupported = minorVersion == 1;
|
||||
imageSupport.postValue(imagesSupported);
|
||||
if (!imagesSupported) return;
|
||||
|
||||
// check if we should show onboarding, only if images are supported
|
||||
Settings settings =
|
||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||
if (settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) {
|
||||
showImageOnboarding.postValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
void imageOnboardingSeen() {
|
||||
dbExecutor.execute(() -> {
|
||||
try {
|
||||
Settings settings = new Settings();
|
||||
settings.putBoolean(SHOW_ONBOARDING_IMAGE, false);
|
||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void storeAttachments(GroupId groupId, @Nullable String text,
|
||||
List<Uri> uris, long timestamp) {
|
||||
dbExecutor.execute(() -> {
|
||||
@@ -262,6 +314,14 @@ public class ConversationViewModel extends AndroidViewModel {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
LiveData<Boolean> hasImageSupport() {
|
||||
return imageSupport;
|
||||
}
|
||||
|
||||
LiveData<Boolean> showImageOnboarding() {
|
||||
return showImageOnboarding;
|
||||
}
|
||||
|
||||
LiveData<Boolean> isContactDeleted() {
|
||||
return contactDeleted;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.briar.android.view;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
@@ -8,6 +10,7 @@ import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.view.AbsSavedState;
|
||||
import android.support.v7.app.AlertDialog.Builder;
|
||||
import android.support.v7.widget.AppCompatImageButton;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -18,11 +21,15 @@ import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
|
||||
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.PromptStateChangeListener;
|
||||
|
||||
import static android.content.Intent.ACTION_GET_CONTENT;
|
||||
import static android.content.Intent.ACTION_OPEN_DOCUMENT;
|
||||
import static android.content.Intent.CATEGORY_OPENABLE;
|
||||
import static android.content.Intent.EXTRA_ALLOW_MULTIPLE;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.support.v4.content.ContextCompat.getColor;
|
||||
import static android.support.v4.view.AbsSavedState.EMPTY_STATE;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
@@ -30,6 +37,9 @@ import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_DISMISSED;
|
||||
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_FINISHED;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
@@ -42,6 +52,7 @@ public class TextAttachmentController extends TextSendController
|
||||
private final AttachImageListener imageListener;
|
||||
|
||||
private CharSequence textHint;
|
||||
private boolean hasImageSupport = false;
|
||||
private List<Uri> imageUris = emptyList();
|
||||
|
||||
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
|
||||
@@ -78,7 +89,28 @@ public class TextAttachmentController extends TextSendController
|
||||
return !imageUris.isEmpty();
|
||||
}
|
||||
|
||||
/***
|
||||
* By default, image support is disabled.
|
||||
* Once you know that it is supported in the current context,
|
||||
* call this method to enable it.
|
||||
*/
|
||||
public void setImagesSupported() {
|
||||
hasImageSupport = true;
|
||||
imageButton.setImageResource(R.drawable.ic_image);
|
||||
}
|
||||
|
||||
private void onImageButtonClicked() {
|
||||
if (!hasImageSupport) {
|
||||
Context ctx = imageButton.getContext();
|
||||
Builder builder = new Builder(ctx, R.style.OnboardingDialogTheme);
|
||||
builder.setTitle(
|
||||
ctx.getString(R.string.dialog_title_no_image_support));
|
||||
builder.setMessage(
|
||||
ctx.getString(R.string.dialog_message_no_image_support));
|
||||
builder.setPositiveButton(R.string.got_it, null);
|
||||
builder.show();
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(SDK_INT >= 19 ?
|
||||
ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
|
||||
intent.addCategory(CATEGORY_OPENABLE);
|
||||
@@ -187,6 +219,24 @@ public class TextAttachmentController extends TextSendController
|
||||
reset();
|
||||
}
|
||||
|
||||
public void showImageOnboarding(Activity activity, Runnable onOnboardingSeen) {
|
||||
PromptStateChangeListener listener = (prompt, state) -> {
|
||||
if (state == STATE_DISMISSED || state == STATE_FINISHED) {
|
||||
onOnboardingSeen.run();
|
||||
}
|
||||
};
|
||||
int color = resolveColorAttribute(activity, R.attr.colorControlNormal);
|
||||
MaterialTapTargetPrompt p = new MaterialTapTargetPrompt.Builder(activity,
|
||||
R.style.OnboardingDialogTheme).setTarget(imageButton)
|
||||
.setPrimaryText(R.string.dialog_title_image_support)
|
||||
.setSecondaryText(R.string.dialog_message_image_support)
|
||||
.setBackgroundColour(getColor(activity, R.color.briar_primary))
|
||||
.setIcon(R.drawable.ic_image)
|
||||
.setIconDrawableColourFilter(color)
|
||||
.setPromptStateChangeListener(listener)
|
||||
.show();
|
||||
}
|
||||
|
||||
private static class SavedState extends AbsSavedState {
|
||||
|
||||
@Nullable
|
||||
|
||||
Reference in New Issue
Block a user