diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/FeatureFlags.java b/bramble-api/src/main/java/org/briarproject/bramble/api/FeatureFlags.java index 1f03daea6..daf9858db 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/FeatureFlags.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/FeatureFlags.java @@ -6,4 +6,7 @@ package org.briarproject.bramble.api; public interface FeatureFlags { boolean shouldEnableImageAttachments(); + + boolean shouldEnableProfilePictures(); + } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java index 8b631802e..4ac540199 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/test/BrambleCoreIntegrationTestModule.java @@ -22,6 +22,17 @@ public class BrambleCoreIntegrationTestModule { @Provides FeatureFlags provideFeatureFlags() { - return () -> true; + return new FeatureFlags() { + + @Override + public boolean shouldEnableImageAttachments() { + return true; + } + + @Override + public boolean shouldEnableProfilePictures() { + return true; + } + }; } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java index 45c90381e..f501fe347 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java @@ -33,6 +33,7 @@ import org.briarproject.briar.android.forum.ForumModule; import org.briarproject.briar.android.keyagreement.ContactExchangeModule; import org.briarproject.briar.android.login.LoginModule; import org.briarproject.briar.android.navdrawer.NavDrawerModule; +import org.briarproject.briar.android.settings.SettingsModule; import org.briarproject.briar.android.privategroup.list.GroupListModule; import org.briarproject.briar.android.reporting.DevReportModule; import org.briarproject.briar.android.test.TestAvatarCreatorImpl; @@ -70,6 +71,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; LoginModule.class, NavDrawerModule.class, ViewModelModule.class, + SettingsModule.class, DevReportModule.class, ContactListModule.class, // below need to be within same scope as ViewModelProvider.Factory @@ -255,6 +257,17 @@ public class AppModule { @Provides FeatureFlags provideFeatureFlags() { - return () -> IS_DEBUG_BUILD; + return new FeatureFlags() { + + @Override + public boolean shouldEnableImageAttachments() { + return IS_DEBUG_BUILD; + } + + @Override + public boolean shouldEnableProfilePictures() { + return IS_DEBUG_BUILD; + } + }; } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index 11e5d02a7..3835a7aaa 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -67,6 +67,7 @@ import org.briarproject.briar.android.privategroup.reveal.RevealContactsFragment import org.briarproject.briar.android.reporting.CrashFragment; import org.briarproject.briar.android.reporting.CrashReportActivity; import org.briarproject.briar.android.reporting.ReportFormFragment; +import org.briarproject.briar.android.settings.ConfirmAvatarDialogFragment; import org.briarproject.briar.android.settings.SettingsActivity; import org.briarproject.briar.android.settings.SettingsFragment; import org.briarproject.briar.android.sharing.BlogInvitationActivity; @@ -239,4 +240,6 @@ public interface ActivityComponent { void inject(CrashFragment crashFragment); + void inject(ConfirmAvatarDialogFragment fragment); + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java index 3e1351ab3..58ab2ef0c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/RequestCodes.java @@ -16,5 +16,6 @@ public interface RequestCodes { int REQUEST_KEYGUARD_UNLOCK = 12; int REQUEST_ATTACH_IMAGE = 13; int REQUEST_SAVE_ATTACHMENT = 14; + int REQUEST_AVATAR_IMAGE = 15; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentCreationTask.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentCreationTask.java index 031480b38..01cdb832a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentCreationTask.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentCreationTask.java @@ -105,10 +105,10 @@ class AttachmentCreationTask { if (is == null) throw new IOException(); is = imageCompressor .compressImage(is, contentType); - contentType = "image/jpeg"; long timestamp = System.currentTimeMillis(); AttachmentHeader h = messagingManager - .addLocalAttachment(groupId, timestamp, contentType, is); + .addLocalAttachment(groupId, timestamp, + ImageCompressor.MIME_TYPE, is); tryToClose(is, LOG, WARNING); logDuration(LOG, "Storing attachment", start); return h; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageCompressor.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageCompressor.java index 8c1e99ad9..505d9bd50 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageCompressor.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageCompressor.java @@ -8,10 +8,15 @@ import java.io.InputStream; public interface ImageCompressor { + /** + * The MIME type of compressed images + */ + String MIME_TYPE = "image/jpeg"; + /** * Load an image from {@code is}, compress it and return an InputStream * from which the resulting image can be read. The image will be compressed - * such that it fits into a message. + * as a JPEG image such that it fits into a message. * * @param is the stream to read the source image from * @param contentType the mimetype of the source image such as "image/jpeg" @@ -23,8 +28,8 @@ public interface ImageCompressor { /** * Compress an image and return an InputStream from which the resulting - * image can be read. The image will be compressed such that it fits into - * a message. + * image can be read. The image will be compressed as a JPEG image such that + * it fits into a message. * * @param bitmap the source image * @return a stream from which the resulting image can be read diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/ConfirmAvatarDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/ConfirmAvatarDialogFragment.java new file mode 100644 index 000000000..588c63954 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/ConfirmAvatarDialogFragment.java @@ -0,0 +1,90 @@ +package org.briarproject.briar.android.settings; + +import android.app.Dialog; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import org.briarproject.briar.R; +import org.briarproject.briar.android.activity.BaseActivity; + +import javax.inject.Inject; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.ViewModelProvider; + +import static java.util.Objects.requireNonNull; + +public class ConfirmAvatarDialogFragment extends DialogFragment { + + final static String TAG = ConfirmAvatarDialogFragment.class.getName(); + + @Inject + ViewModelProvider.Factory viewModelFactory; + private SettingsViewModel settingsViewModel; + + private static final String ARG_URI = "uri"; + private Uri uri; + + static ConfirmAvatarDialogFragment newInstance(Uri uri) { + ConfirmAvatarDialogFragment f = new ConfirmAvatarDialogFragment(); + + Bundle args = new Bundle(); + args.putString(ARG_URI, uri.toString()); + f.setArguments(args); + + return f; + } + + @Override + public void onAttach(Context ctx) { + super.onAttach(ctx); + ((BaseActivity) requireActivity()).getActivityComponent().inject(this); + } + + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle args = requireArguments(); + String argUri = requireNonNull(args.getString(ARG_URI)); + uri = Uri.parse(argUri); + + FragmentActivity activity = requireActivity(); + + ViewModelProvider provider = + new ViewModelProvider(activity, viewModelFactory); + settingsViewModel = provider.get(SettingsViewModel.class); + settingsViewModel.onCreate(); + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + + LayoutInflater factory = LayoutInflater.from(getContext()); + final View view = + factory.inflate(R.layout.fragment_confirm_avatar_dialog, null); + builder.setView(view); + + builder.setTitle(R.string.dialog_confirm_profile_picture_title) + .setMessage(R.string.dialog_confirm_profile_picture_question); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.dialog_confirm_profile_picture_set, + (dialog, id) -> { + settingsViewModel.setAvatar(uri); + }); + + ImageView imageView = view.findViewById(R.id.image); + imageView.setImageResource(R.drawable.contact_connected); + imageView.setImageURI(uri); + + settingsViewModel.getOwnIdentityInfo().observe(activity, us -> { + TextView textViewUserName = view.findViewById(R.id.username); + textViewUserName.setText(us.getLocalAuthor().getName()); + }); + + return builder.create(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/OwnIdentityInfo.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/OwnIdentityInfo.java new file mode 100644 index 000000000..2a8d4a655 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/OwnIdentityInfo.java @@ -0,0 +1,26 @@ +package org.briarproject.briar.android.settings; + +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.api.identity.AuthorInfo; + +@NotNullByDefault +class OwnIdentityInfo { + + private final LocalAuthor localAuthor; + private final AuthorInfo authorInfo; + + OwnIdentityInfo(LocalAuthor localAuthor, AuthorInfo authorInfo) { + this.localAuthor = localAuthor; + this.authorInfo = authorInfo; + } + + LocalAuthor getLocalAuthor() { + return localAuthor; + } + + AuthorInfo getAuthorInfo() { + return authorInfo; + } + +} \ No newline at end of file diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java index 311711b1b..9e090a62f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java @@ -1,16 +1,37 @@ package org.briarproject.briar.android.settings; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; +import org.briarproject.bramble.api.FeatureFlags; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; +import org.briarproject.briar.android.util.UiUtils; +import org.briarproject.briar.android.view.AuthorView; +import javax.inject.Inject; + +import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; +import androidx.lifecycle.ViewModelProvider; +import de.hdodenhof.circleimageview.CircleImageView; + +import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_AVATAR_IMAGE; public class SettingsActivity extends BriarActivity { + @Inject + ViewModelProvider.Factory viewModelFactory; + private SettingsViewModel settingsViewModel; + + @Inject + FeatureFlags featureFlags; + @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); @@ -22,6 +43,32 @@ public class SettingsActivity extends BriarActivity { } setContentView(R.layout.activity_settings); + + if (featureFlags.shouldEnableProfilePictures()) { + ViewModelProvider provider = + new ViewModelProvider(this, viewModelFactory); + settingsViewModel = provider.get(SettingsViewModel.class); + settingsViewModel.onCreate(); + + settingsViewModel.getOwnIdentityInfo().observe(this, us -> { + TextView textViewUserName = findViewById(R.id.username); + textViewUserName.setText(us.getLocalAuthor().getName()); + + CircleImageView imageViewAvatar = + findViewById(R.id.avatarImage); + AuthorView + .setAvatar(imageViewAvatar, us.getLocalAuthor().getId(), + us.getAuthorInfo()); + }); + + View avatarGroup = findViewById(R.id.avatarGroup); + avatarGroup.setOnClickListener(e -> { + selectAvatarImage(); + }); + } else { + View view = findViewById(R.id.avatarGroup); + view.setVisibility(View.GONE); + } } @Override @@ -37,4 +84,31 @@ public class SettingsActivity extends BriarActivity { } return false; } + + private void selectAvatarImage() { + Intent intent = UiUtils.createSelectImageIntent(false); + startActivityForResult(intent, REQUEST_AVATAR_IMAGE); + } + + @Override + protected void onActivityResult(int request, int result, + @Nullable Intent data) { + super.onActivityResult(request, result, data); + + if (request == REQUEST_AVATAR_IMAGE && result == RESULT_OK) { + onAvatarImageReceived(data); + } + } + + private void onAvatarImageReceived(@Nullable Intent resultData) { + if (resultData == null) return; + Uri uri = resultData.getData(); + if (uri == null) return; + + ConfirmAvatarDialogFragment dialog = + ConfirmAvatarDialogFragment.newInstance(uri); + dialog.show(getSupportFragmentManager(), + ConfirmAvatarDialogFragment.TAG); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsModule.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsModule.java new file mode 100644 index 000000000..ed63c309a --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsModule.java @@ -0,0 +1,19 @@ +package org.briarproject.briar.android.settings; + +import org.briarproject.briar.android.viewmodel.ViewModelKey; + +import androidx.lifecycle.ViewModel; +import dagger.Binds; +import dagger.Module; +import dagger.multibindings.IntoMap; + +@Module +public abstract class SettingsModule { + + @Binds + @IntoMap + @ViewModelKey(SettingsViewModel.class) + abstract ViewModel bindSettingsViewModel( + SettingsViewModel settingsViewModel); + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java new file mode 100644 index 000000000..d21e93b58 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java @@ -0,0 +1,116 @@ +package org.briarproject.briar.android.settings; + +import android.app.Application; +import android.content.ContentResolver; +import android.net.Uri; + +import org.briarproject.bramble.api.db.DatabaseExecutor; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.identity.IdentityManager; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.util.LogUtils; +import org.briarproject.briar.android.attachment.ImageCompressor; +import org.briarproject.briar.api.avatar.AvatarManager; +import org.briarproject.briar.api.identity.AuthorInfo; +import org.briarproject.briar.api.identity.AuthorManager; +import org.jsoup.UnsupportedMimeTypeException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import static java.util.Arrays.asList; +import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes; + +@NotNullByDefault +public class SettingsViewModel extends AndroidViewModel { + + private final static Logger LOG = + getLogger(SettingsViewModel.class.getName()); + + private final IdentityManager identityManager; + private final AvatarManager avatarManager; + private final AuthorManager authorManager; + private final ImageCompressor imageCompressor; + @DatabaseExecutor + private final Executor dbExecutor; + + private final MutableLiveData ownIdentityInfo = + new MutableLiveData<>(); + + @Inject + SettingsViewModel(Application application, + IdentityManager identityManager, + AvatarManager avatarManager, + AuthorManager authorManager, + ImageCompressor imageCompressor, + @DatabaseExecutor Executor dbExecutor) { + super(application); + this.identityManager = identityManager; + this.imageCompressor = imageCompressor; + this.avatarManager = avatarManager; + this.authorManager = authorManager; + this.dbExecutor = dbExecutor; + } + + void onCreate() { + if (ownIdentityInfo.getValue() == null) loadOwnIdentityInfo(); + } + + LiveData getOwnIdentityInfo() { + return ownIdentityInfo; + } + + private void loadOwnIdentityInfo() { + dbExecutor.execute(() -> { + try { + LocalAuthor localAuthor = identityManager.getLocalAuthor(); + AuthorInfo authorInfo = authorManager.getMyAuthorInfo(); + ownIdentityInfo + .postValue( + new OwnIdentityInfo(localAuthor, authorInfo)); + } catch (DbException e) { + LogUtils.logException(LOG, Level.WARNING, e); + } + }); + } + + void setAvatar(Uri uri) { + dbExecutor.execute(() -> { + try { + trySetAvatar(uri); + } catch (IOException | DbException e) { + LogUtils.logException(LOG, Level.WARNING, e); + } + }); + } + + private void trySetAvatar(Uri uri) throws IOException, DbException { + ContentResolver contentResolver = + getApplication().getContentResolver(); + String contentType = contentResolver.getType(uri); + if (contentType == null) throw new IOException("null content type"); + if (!asList(getSupportedImageContentTypes()).contains(contentType)) { + String uriString = uri.toString(); + throw new UnsupportedMimeTypeException("", contentType, uriString); + } + InputStream is = contentResolver.openInputStream(uri); + if (is == null) throw new IOException( + "ContentResolver returned null when opening InputStream"); + is = imageCompressor + .compressImage(is, contentType); + avatarManager.addAvatar(ImageCompressor.MIME_TYPE, is); + loadOwnIdentityInfo(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index dda2fdb60..404b7e621 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -57,7 +57,12 @@ import androidx.lifecycle.Observer; import static android.content.Context.KEYGUARD_SERVICE; import static android.content.Context.POWER_SERVICE; +import static android.content.Intent.ACTION_GET_CONTENT; +import static android.content.Intent.ACTION_OPEN_DOCUMENT; import static android.content.Intent.CATEGORY_DEFAULT; +import static android.content.Intent.CATEGORY_OPENABLE; +import static android.content.Intent.EXTRA_ALLOW_MULTIPLE; +import static android.content.Intent.EXTRA_MIME_TYPES; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.Build.MANUFACTURER; import static android.os.Build.VERSION.SDK_INT; @@ -89,6 +94,7 @@ import static androidx.core.graphics.drawable.DrawableCompat.setTint; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_RTL; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.DAYS; +import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes; import static org.briarproject.briar.BuildConfig.APPLICATION_ID; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME; @@ -250,6 +256,18 @@ public class UiUtils { }; } + public static Intent createSelectImageIntent(boolean allowMultiple) { + Intent intent = new Intent(SDK_INT >= 19 ? + ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT); + intent.setType("image/*"); + intent.addCategory(CATEGORY_OPENABLE); + if (SDK_INT >= 19) + intent.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes()); + if (allowMultiple && SDK_INT >= 18) + intent.putExtra(EXTRA_ALLOW_MULTIPLE, true); + return intent; + } + public static void showOnboardingDialog(Context ctx, String text) { new AlertDialog.Builder(ctx, R.style.OnboardingDialogTheme) .setMessage(text) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java index 806fd9e4c..2534a3cdc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java @@ -14,6 +14,7 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.attachment.AttachmentItemResult; import org.briarproject.briar.android.attachment.AttachmentManager; import org.briarproject.briar.android.attachment.AttachmentResult; +import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener; import java.util.ArrayList; @@ -29,18 +30,12 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; -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.content.Intent.EXTRA_MIME_TYPES; import static android.os.Build.VERSION.SDK_INT; import static android.view.View.GONE; import static android.widget.Toast.LENGTH_LONG; import static androidx.core.content.ContextCompat.getColor; import static androidx.customview.view.AbsSavedState.EMPTY_STATE; import static androidx.lifecycle.Lifecycle.State.DESTROYED; -import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes; import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE; @@ -127,14 +122,7 @@ public class TextAttachmentController extends TextSendController } private Intent getAttachFileIntent() { - Intent intent = new Intent(SDK_INT >= 19 ? - ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT); - intent.setType("image/*"); - intent.addCategory(CATEGORY_OPENABLE); - if (SDK_INT >= 19) - intent.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes()); - if (SDK_INT >= 18) intent.putExtra(EXTRA_ALLOW_MULTIPLE, true); - return intent; + return UiUtils.createSelectImageIntent(true); } /** diff --git a/briar-android/src/main/res/layout/activity_settings.xml b/briar-android/src/main/res/layout/activity_settings.xml index f5c555431..1dafd9f04 100644 --- a/briar-android/src/main/res/layout/activity_settings.xml +++ b/briar-android/src/main/res/layout/activity_settings.xml @@ -1,11 +1,87 @@ - - - \ No newline at end of file + android:layout_height="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/fragment_confirm_avatar_dialog.xml b/briar-android/src/main/res/layout/fragment_confirm_avatar_dialog.xml new file mode 100644 index 000000000..a36219976 --- /dev/null +++ b/briar-android/src/main/res/layout/fragment_confirm_avatar_dialog.xml @@ -0,0 +1,42 @@ + + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 9d7e56b68..38bd7bfad 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -453,6 +453,13 @@ No RSS feeds to show\n\nTap the + icon to import a feed There was a problem loading your feeds. Please try again later. + + Tap to change your profile picture + Please confirm + Do you want to use this image as your profile picture? + Only your contacts can see your profile image + Set picture + Language & region This setting will take effect when you restart Briar. Please sign out and restart Briar. diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt index 27d185c5b..5b0007816 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt @@ -88,5 +88,8 @@ internal class HeadlessModule(private val appDir: File) { internal fun provideObjectMapper() = ObjectMapper() @Provides - internal fun provideFeatureFlags() = FeatureFlags { false } + internal fun provideFeatureFlags() = object : FeatureFlags { + override fun shouldEnableImageAttachments() = false + override fun shouldEnableProfilePictures() = false + } } diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt index ff1326aaf..70ea2323b 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt @@ -76,7 +76,10 @@ internal class HeadlessTestModule(private val appDir: File) { internal fun provideObjectMapper() = ObjectMapper() @Provides - internal fun provideFeatureFlags() = FeatureFlags { false } + internal fun provideFeatureFlags() = object : FeatureFlags { + override fun shouldEnableImageAttachments() = false + override fun shouldEnableProfilePictures() = false + } @Provides internal fun provideTestAvatarCreator() = TestAvatarCreator { null }