Move avatar layout into own preference

which is only shown on main settings fragment
This commit is contained in:
Torsten Grote
2021-01-22 17:03:09 -03:00
parent f0685c4a43
commit 58d9deb3b8
9 changed files with 230 additions and 172 deletions

View File

@@ -39,6 +39,7 @@ import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.settings.ConnectionsFragment; import org.briarproject.briar.android.settings.ConnectionsFragment;
import org.briarproject.briar.android.settings.NotificationsFragment; import org.briarproject.briar.android.settings.NotificationsFragment;
import org.briarproject.briar.android.settings.SecurityFragment; import org.briarproject.briar.android.settings.SecurityFragment;
import org.briarproject.briar.android.settings.SettingsFragment;
import org.briarproject.briar.android.view.EmojiTextInputView; import org.briarproject.briar.android.view.EmojiTextInputView;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.DozeWatchdog; import org.briarproject.briar.api.android.DozeWatchdog;
@@ -197,6 +198,8 @@ public interface AndroidComponent
void inject(BriarModelLoader briarModelLoader); void inject(BriarModelLoader briarModelLoader);
void inject(SettingsFragment settingsFragment);
void inject(ConnectionsFragment connectionsFragment); void inject(ConnectionsFragment connectionsFragment);
void inject(SecurityFragment securityFragment); void inject(SecurityFragment securityFragment);

View File

@@ -0,0 +1,45 @@
package org.briarproject.briar.android.settings;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import org.briarproject.briar.R;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import de.hdodenhof.circleimageview.CircleImageView;
import static org.briarproject.briar.android.view.AuthorView.setAvatar;
public class AvatarPreference extends Preference {
@Nullable
private OwnIdentityInfo info;
public AvatarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.preference_avatar);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
View v = holder.itemView;
if (info != null) {
TextView textViewUserName = v.findViewById(R.id.username);
CircleImageView imageViewAvatar = v.findViewById(R.id.avatarImage);
textViewUserName.setText(info.getLocalAuthor().getName());
setAvatar(imageViewAvatar, info.getLocalAuthor().getId(),
info.getAuthorInfo());
}
}
void setOwnIdentityInfo(OwnIdentityInfo info) {
this.info = info;
notifyChanged();
}
}

View File

@@ -1,55 +1,31 @@
package org.briarproject.briar.android.settings; package org.briarproject.briar.android.settings;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; 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.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentFactory; import androidx.fragment.app.FragmentFactory;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback; import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback;
import de.hdodenhof.circleimageview.CircleImageView;
import static android.widget.Toast.LENGTH_LONG;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_AVATAR_IMAGE;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class SettingsActivity extends BriarActivity public class SettingsActivity extends BriarActivity
implements OnPreferenceStartFragmentCallback { implements OnPreferenceStartFragmentCallback {
@Inject
ViewModelProvider.Factory viewModelFactory;
@Inject
FeatureFlags featureFlags;
private SettingsViewModel settingsViewModel;
@Override @Override
public void injectActivity(ActivityComponent component) { public void injectActivity(ActivityComponent component) {
component.inject(this); component.inject(this);
settingsViewModel = new ViewModelProvider(this, viewModelFactory)
.get(SettingsViewModel.class);
} }
@Override @Override
@@ -63,33 +39,6 @@ public class SettingsActivity extends BriarActivity
} }
setContentView(R.layout.activity_settings); setContentView(R.layout.activity_settings);
if (featureFlags.shouldEnableProfilePictures()) {
TextView textViewUserName = findViewById(R.id.username);
CircleImageView imageViewAvatar =
findViewById(R.id.avatarImage);
settingsViewModel.getOwnIdentityInfo().observe(this, us -> {
textViewUserName.setText(us.getLocalAuthor().getName());
AuthorView.setAvatar(imageViewAvatar,
us.getLocalAuthor().getId(), us.getAuthorInfo());
});
settingsViewModel.getSetAvatarFailed()
.observeEvent(this, failed -> {
if (failed) {
Toast.makeText(this,
R.string.change_profile_picture_failed_message,
LENGTH_LONG).show();
}
});
View avatarGroup = findViewById(R.id.avatarGroup);
avatarGroup.setOnClickListener(e -> selectAvatarImage());
} else {
View view = findViewById(R.id.avatarGroup);
view.setVisibility(View.GONE);
}
} }
@Override @Override
@@ -101,32 +50,6 @@ public class SettingsActivity extends BriarActivity
return false; 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);
}
@Override @Override
public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller,
Preference pref) { Preference pref) {

View File

@@ -1,17 +1,31 @@
package org.briarproject.briar.android.settings; package org.briarproject.briar.android.settings;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import static android.app.Activity.RESULT_OK;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_AVATAR_IMAGE;
import static org.briarproject.briar.android.util.UiUtils.createSelectImageIntent;
import static org.briarproject.briar.android.util.UiUtils.triggerFeedback; import static org.briarproject.briar.android.util.UiUtils.triggerFeedback;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -20,35 +34,88 @@ public class SettingsFragment extends PreferenceFragmentCompat {
public static final String SETTINGS_NAMESPACE = "android-ui"; public static final String SETTINGS_NAMESPACE = "android-ui";
private static final String PREF_KEY_AVATAR = "pref_key_avatar";
private static final String PREF_KEY_FEEDBACK = "pref_key_send_feedback";
private static final String PREF_KEY_DEV = "pref_key_dev";
private static final String PREF_KEY_EXPLODE = "pref_key_explode";
@Inject
ViewModelProvider.Factory viewModelFactory;
private SettingsViewModel viewModel;
private AvatarPreference prefAvatar;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
getAndroidComponent(context).inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(SettingsViewModel.class);
}
@Override @Override
public void onCreatePreferences(Bundle bundle, String s) { public void onCreatePreferences(Bundle bundle, String s) {
addPreferencesFromResource(R.xml.settings); addPreferencesFromResource(R.xml.settings);
prefAvatar = requireNonNull(findPreference(PREF_KEY_AVATAR));
if (viewModel.shouldEnableProfilePictures()) {
prefAvatar.setOnPreferenceClickListener(preference -> {
Intent intent = createSelectImageIntent(false);
startActivityForResult(intent, REQUEST_AVATAR_IMAGE);
return true;
});
} else {
prefAvatar.setVisible(false);
}
Preference prefFeedback = Preference prefFeedback =
requireNonNull(findPreference("pref_key_send_feedback")); requireNonNull(findPreference(PREF_KEY_FEEDBACK));
prefFeedback.setOnPreferenceClickListener(preference -> { prefFeedback.setOnPreferenceClickListener(preference -> {
triggerFeedback(requireContext()); triggerFeedback(requireContext());
return true; return true;
}); });
Preference explode = requireNonNull(findPreference("pref_key_explode")); Preference explode = requireNonNull(findPreference(PREF_KEY_EXPLODE));
if (IS_DEBUG_BUILD) { if (IS_DEBUG_BUILD) {
explode.setOnPreferenceClickListener(preference -> { explode.setOnPreferenceClickListener(preference -> {
throw new RuntimeException("Boom!"); throw new RuntimeException("Boom!");
}); });
} else { } else {
explode.setVisible(false); PreferenceGroup dev = requireNonNull(findPreference(PREF_KEY_DEV));
findPreference("pref_key_test_data").setVisible(false); dev.setVisible(false);
PreferenceGroup testing = explode.getParent();
if (testing == null) throw new AssertionError();
testing.setVisible(false);
} }
} }
@Override
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewModel.getOwnIdentityInfo().observe(getViewLifecycleOwner(), us ->
prefAvatar.setOwnIdentityInfo(us)
);
}
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
requireActivity().setTitle(R.string.settings_button); requireActivity().setTitle(R.string.settings_button);
} }
@Override
public void onActivityResult(int request, int result,
@Nullable Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_AVATAR_IMAGE && result == RESULT_OK) {
if (data == null) return;
Uri uri = data.getData();
if (uri == null) return;
DialogFragment dialog =
ConfirmAvatarDialogFragment.newInstance(uri);
dialog.show(getParentFragmentManager(),
ConfirmAvatarDialogFragment.TAG);
}
}
} }

View File

@@ -3,7 +3,9 @@ package org.briarproject.briar.android.settings;
import android.app.Application; import android.app.Application;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.net.Uri; import android.net.Uri;
import android.widget.Toast;
import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.TransactionManager; import org.briarproject.bramble.api.db.TransactionManager;
@@ -29,8 +31,6 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException; import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException;
import org.briarproject.briar.android.attachment.media.ImageCompressor; import org.briarproject.briar.android.attachment.media.ImageCompressor;
import org.briarproject.briar.android.viewmodel.DbViewModel; import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.avatar.AvatarManager; import org.briarproject.briar.api.avatar.AvatarManager;
import org.briarproject.briar.api.identity.AuthorInfo; import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.identity.AuthorManager; import org.briarproject.briar.api.identity.AuthorManager;
@@ -45,6 +45,7 @@ import javax.inject.Inject;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
@@ -75,6 +76,7 @@ class SettingsViewModel extends DbViewModel implements EventListener {
private final AuthorManager authorManager; private final AuthorManager authorManager;
private final ImageCompressor imageCompressor; private final ImageCompressor imageCompressor;
private final Executor ioExecutor; private final Executor ioExecutor;
private final FeatureFlags featureFlags;
final SettingsStore settingsStore; final SettingsStore settingsStore;
final TorSummaryProvider torSummaryProvider; final TorSummaryProvider torSummaryProvider;
@@ -85,8 +87,6 @@ class SettingsViewModel extends DbViewModel implements EventListener {
private final MutableLiveData<OwnIdentityInfo> ownIdentityInfo = private final MutableLiveData<OwnIdentityInfo> ownIdentityInfo =
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveEvent<Boolean> setAvatarFailed =
new MutableLiveEvent<>();
private final MutableLiveData<Boolean> screenLockEnabled = private final MutableLiveData<Boolean> screenLockEnabled =
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveData<String> screenLockTimeout = private final MutableLiveData<String> screenLockTimeout =
@@ -106,7 +106,8 @@ class SettingsViewModel extends DbViewModel implements EventListener {
ImageCompressor imageCompressor, ImageCompressor imageCompressor,
LocationUtils locationUtils, LocationUtils locationUtils,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
@IoExecutor Executor ioExecutor) { @IoExecutor Executor ioExecutor,
FeatureFlags featureFlags) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor); super(application, dbExecutor, lifecycleManager, db, androidExecutor);
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.identityManager = identityManager; this.identityManager = identityManager;
@@ -115,6 +116,7 @@ class SettingsViewModel extends DbViewModel implements EventListener {
this.avatarManager = avatarManager; this.avatarManager = avatarManager;
this.authorManager = authorManager; this.authorManager = authorManager;
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.featureFlags = featureFlags;
this.settingsStore = new SettingsStore(settingsManager, dbExecutor, this.settingsStore = new SettingsStore(settingsManager, dbExecutor,
SETTINGS_NAMESPACE); SETTINGS_NAMESPACE);
torSummaryProvider = new TorSummaryProvider(getApplication(), torSummaryProvider = new TorSummaryProvider(getApplication(),
@@ -126,7 +128,7 @@ class SettingsViewModel extends DbViewModel implements EventListener {
eventBus.addListener(this); eventBus.addListener(this);
loadSettings(); loadSettings();
loadOwnIdentityInfo(); if (shouldEnableProfilePictures()) loadOwnIdentityInfo();
} }
@Override @Override
@@ -154,6 +156,10 @@ class SettingsViewModel extends DbViewModel implements EventListener {
}); });
} }
boolean shouldEnableProfilePictures() {
return featureFlags.shouldEnableProfilePictures();
}
private void loadOwnIdentityInfo() { private void loadOwnIdentityInfo() {
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
@@ -206,7 +212,7 @@ class SettingsViewModel extends DbViewModel implements EventListener {
trySetAvatar(uri); trySetAvatar(uri);
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
setAvatarFailed.postEvent(true); onSetAvatarFailed();
} }
}); });
} }
@@ -230,17 +236,19 @@ class SettingsViewModel extends DbViewModel implements EventListener {
loadOwnIdentityInfo(); loadOwnIdentityInfo();
} catch (IOException | DbException e) { } catch (IOException | DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
setAvatarFailed.postEvent(true); onSetAvatarFailed();
} }
}); });
} }
LiveData<OwnIdentityInfo> getOwnIdentityInfo() { private void onSetAvatarFailed() {
return ownIdentityInfo; Toast.makeText(getApplication(),
R.string.change_profile_picture_failed_message,
LENGTH_LONG).show();
} }
LiveEvent<Boolean> getSetAvatarFailed() { LiveData<OwnIdentityInfo> getOwnIdentityInfo() {
return setAvatarFailed; return ownIdentityInfo;
} }
LiveData<Boolean> getScreenLockEnabled() { LiveData<Boolean> getScreenLockEnabled() {

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:attr/textColorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM13,14h-2v-2h2v2zM13,10h-2L11,6h2v4z" />
</vector>

View File

@@ -1,78 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/fragmentContainer"
android:name="org.briarproject.briar.android.settings.SettingsFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/avatarGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarImage"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_margin="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:paddingStart="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toTopOf="@+id/avatarExplanation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/avatarImage"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="username" />
<TextView
android:id="@+id/avatarExplanation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:paddingStart="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium"
android:text="@string/change_profile_picture"
android:textColor="@color/briar_text_secondary_inverse"
android:textSize="@dimen/text_size_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/avatarImage"
app:layout_constraintTop_toBottomOf="@+id/username" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainer"
android:name="org.briarproject.briar.android.settings.SettingsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout_width="800dp"
tools:layout_height="75dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/avatarGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarImage"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_margin="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:paddingStart="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toTopOf="@+id/avatarExplanation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/avatarImage"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="username" />
<TextView
android:id="@+id/avatarExplanation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:paddingStart="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium"
android:text="@string/change_profile_picture"
android:textColor="@color/briar_text_secondary_inverse"
android:textSize="@dimen/text_size_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/avatarImage"
app:layout_constraintTop_toBottomOf="@+id/username" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>

View File

@@ -2,6 +2,8 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<org.briarproject.briar.android.settings.AvatarPreference android:key="pref_key_avatar" />
<Preference <Preference
android:title="@string/display_settings_title" android:title="@string/display_settings_title"
app:fragment="org.briarproject.briar.android.settings.DisplayFragment" app:fragment="org.briarproject.briar.android.settings.DisplayFragment"
@@ -24,9 +26,11 @@
<Preference <Preference
android:key="pref_key_send_feedback" android:key="pref_key_send_feedback"
android:title="@string/send_feedback" /> android:title="@string/send_feedback"
app:icon="@drawable/ic_feedback" />
<PreferenceCategory <PreferenceCategory
android:key="pref_key_dev"
android:layout="@layout/preferences_category" android:layout="@layout/preferences_category"
android:title="Developer Options" android:title="Developer Options"
app:allowDividerAbove="true"> app:allowDividerAbove="true">