mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 08:39:22 +01:00
Add avatar picker and defaults.
This commit is contained in:
committed by
Greyson Parrelli
parent
0093e1d3eb
commit
ed23c3fe7c
@@ -9,6 +9,7 @@ import androidx.core.util.Consumer;
|
||||
|
||||
import org.signal.core.util.StreamUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
@@ -34,6 +35,11 @@ class EditGroupProfileRepository implements EditProfileRepository {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCurrentAvatarColor(@NonNull Consumer<AvatarColor> avatarColorConsumer) {
|
||||
SimpleTask.run(() -> Recipient.resolved(getRecipientId()).getAvatarColor(), avatarColorConsumer::accept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCurrentProfileName(@NonNull Consumer<ProfileName> profileNameConsumer) {
|
||||
profileNameConsumer.accept(ProfileName.EMPTY);
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.navigation.NavDirections;
|
||||
import androidx.navigation.NavGraph;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.BaseActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -61,10 +62,10 @@ public class EditProfileActivity extends BaseActivity implements EditProfileFrag
|
||||
setContentView(R.layout.profile_create_activity);
|
||||
|
||||
if (bundle == null) {
|
||||
Bundle extras = getIntent().getExtras();
|
||||
NavGraph graph = Navigation.findNavController(this, R.id.nav_host_fragment).getGraph();
|
||||
|
||||
Navigation.findNavController(this, R.id.nav_host_fragment).setGraph(graph, extras != null ? extras : new Bundle());
|
||||
NavHostFragment fragment = NavHostFragment.create(R.navigation.edit_profile, getIntent().getExtras());
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.fragment_container, fragment)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -20,7 +21,9 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import com.airbnb.lottie.SimpleColorFilter;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.dd.CircularProgressButton;
|
||||
|
||||
@@ -29,11 +32,10 @@ import org.signal.core.util.StreamUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.avatar.Avatars;
|
||||
import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity;
|
||||
import org.thoughtcrime.securesms.mediasend.AvatarSelectionBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.groups.ParcelableGroupId;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.profiles.manage.EditProfileNameFragment;
|
||||
@@ -47,7 +49,6 @@ import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static org.thoughtcrime.securesms.profiles.edit.EditProfileActivity.EXCLUDE_SYSTEM;
|
||||
import static org.thoughtcrime.securesms.profiles.edit.EditProfileActivity.GROUP_ID;
|
||||
import static org.thoughtcrime.securesms.profiles.edit.EditProfileActivity.NEXT_BUTTON_TEXT;
|
||||
@@ -57,7 +58,6 @@ import static org.thoughtcrime.securesms.profiles.edit.EditProfileActivity.SHOW_
|
||||
public class EditProfileFragment extends LoggingFragment {
|
||||
|
||||
private static final String TAG = Log.tag(EditProfileFragment.class);
|
||||
private static final short REQUEST_CODE_SELECT_AVATAR = 31726;
|
||||
private static final int MAX_DESCRIPTION_GLYPHS = 480;
|
||||
private static final int MAX_DESCRIPTION_BYTES = 8192;
|
||||
|
||||
@@ -69,6 +69,8 @@ public class EditProfileFragment extends LoggingFragment {
|
||||
private EditText familyName;
|
||||
private View reveal;
|
||||
private TextView preview;
|
||||
private ImageView avatarPreviewBackground;
|
||||
private ImageView avatarPreview;
|
||||
|
||||
private Intent nextIntent;
|
||||
|
||||
@@ -100,45 +102,38 @@ public class EditProfileFragment extends LoggingFragment {
|
||||
initializeResources(view, groupId);
|
||||
initializeProfileAvatar();
|
||||
initializeProfileName();
|
||||
|
||||
getParentFragmentManager().setFragmentResultListener(AvatarPickerFragment.REQUEST_KEY_SELECT_AVATAR, getViewLifecycleOwner(), (key, bundle) -> {
|
||||
Media media = bundle.getParcelable(AvatarPickerFragment.SELECT_AVATAR_MEDIA);
|
||||
handleMediaFromResult(media);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
private void handleMediaFromResult(@NonNull Media media) {
|
||||
SimpleTask.run(() -> {
|
||||
try {
|
||||
InputStream stream = BlobProvider.getInstance().getStream(requireContext(), media.getUri());
|
||||
|
||||
if (requestCode == REQUEST_CODE_SELECT_AVATAR && resultCode == RESULT_OK) {
|
||||
|
||||
if (data != null && data.getBooleanExtra("delete", false)) {
|
||||
viewModel.setAvatar(null);
|
||||
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_solid_white_24).asDrawable(requireActivity(), AvatarColor.UNKNOWN.colorInt()));
|
||||
return;
|
||||
return StreamUtil.readFully(stream);
|
||||
} catch (IOException ioException) {
|
||||
Log.w(TAG, ioException);
|
||||
return null;
|
||||
}
|
||||
|
||||
SimpleTask.run(() -> {
|
||||
try {
|
||||
Media result = data.getParcelableExtra(AvatarSelectionActivity.EXTRA_MEDIA);
|
||||
InputStream stream = BlobProvider.getInstance().getStream(requireContext(), result.getUri());
|
||||
|
||||
return StreamUtil.readFully(stream);
|
||||
} catch (IOException ioException) {
|
||||
Log.w(TAG, ioException);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
(avatarBytes) -> {
|
||||
if (avatarBytes != null) {
|
||||
viewModel.setAvatar(avatarBytes);
|
||||
GlideApp.with(EditProfileFragment.this)
|
||||
.load(avatarBytes)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.circleCrop()
|
||||
.into(avatar);
|
||||
} else {
|
||||
Toast.makeText(requireActivity(), R.string.CreateProfileActivity_error_setting_profile_photo, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
(avatarBytes) -> {
|
||||
if (avatarBytes != null) {
|
||||
viewModel.setAvatarMedia(media);
|
||||
viewModel.setAvatar(avatarBytes);
|
||||
GlideApp.with(EditProfileFragment.this)
|
||||
.load(avatarBytes)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.circleCrop()
|
||||
.into(avatar);
|
||||
} else {
|
||||
Toast.makeText(requireActivity(), R.string.CreateProfileActivity_error_setting_profile_photo, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeViewModel(boolean excludeSystem, @Nullable GroupId groupId, boolean hasSavedInstanceState) {
|
||||
@@ -160,15 +155,17 @@ public class EditProfileFragment extends LoggingFragment {
|
||||
Bundle arguments = requireArguments();
|
||||
boolean isEditingGroup = groupId != null;
|
||||
|
||||
this.toolbar = view.findViewById(R.id.toolbar);
|
||||
this.title = view.findViewById(R.id.title);
|
||||
this.avatar = view.findViewById(R.id.avatar);
|
||||
this.givenName = view.findViewById(R.id.given_name);
|
||||
this.familyName = view.findViewById(R.id.family_name);
|
||||
this.finishButton = view.findViewById(R.id.finish_button);
|
||||
this.reveal = view.findViewById(R.id.reveal);
|
||||
this.preview = view.findViewById(R.id.name_preview);
|
||||
this.nextIntent = arguments.getParcelable(NEXT_INTENT);
|
||||
this.toolbar = view.findViewById(R.id.toolbar);
|
||||
this.title = view.findViewById(R.id.title);
|
||||
this.avatar = view.findViewById(R.id.avatar);
|
||||
this.givenName = view.findViewById(R.id.given_name);
|
||||
this.familyName = view.findViewById(R.id.family_name);
|
||||
this.finishButton = view.findViewById(R.id.finish_button);
|
||||
this.reveal = view.findViewById(R.id.reveal);
|
||||
this.preview = view.findViewById(R.id.name_preview);
|
||||
this.avatarPreviewBackground = view.findViewById(R.id.avatar_background);
|
||||
this.avatarPreview = view.findViewById(R.id.avatar_placeholder);
|
||||
this.nextIntent = arguments.getParcelable(NEXT_INTENT);
|
||||
|
||||
this.avatar.setOnClickListener(v -> startAvatarSelection());
|
||||
|
||||
@@ -255,6 +252,13 @@ public class EditProfileFragment extends LoggingFragment {
|
||||
.circleCrop()
|
||||
.into(avatar);
|
||||
});
|
||||
|
||||
viewModel.avatarColor().observe(getViewLifecycleOwner(), avatarColor -> {
|
||||
Avatars.ForegroundColor foregroundColor = Avatars.getForegroundColor(avatarColor);
|
||||
|
||||
avatarPreview.getDrawable().setColorFilter(new SimpleColorFilter(foregroundColor.getColorInt()));
|
||||
avatarPreviewBackground.getDrawable().setColorFilter(new SimpleColorFilter(avatarColor.colorInt()));
|
||||
});
|
||||
}
|
||||
|
||||
private static void updateFieldIfNeeded(@NonNull EditText field, @NonNull String value) {
|
||||
@@ -273,11 +277,12 @@ public class EditProfileFragment extends LoggingFragment {
|
||||
}
|
||||
|
||||
private void startAvatarSelection() {
|
||||
AvatarSelectionBottomSheetDialogFragment.create(viewModel.canRemoveProfilePhoto(),
|
||||
true,
|
||||
REQUEST_CODE_SELECT_AVATAR,
|
||||
viewModel.isGroup())
|
||||
.show(getChildFragmentManager(), null);
|
||||
if (viewModel.isGroup()) {
|
||||
Parcelable groupId = ParcelableGroupId.from(viewModel.getGroupId());
|
||||
Navigation.findNavController(requireView()).navigate(EditProfileFragmentDirections.actionCreateProfileFragmentToAvatarPicker((ParcelableGroupId) groupId, viewModel.getAvatarMedia()));
|
||||
} else {
|
||||
Navigation.findNavController(requireView()).navigate(EditProfileFragmentDirections.actionCreateProfileFragmentToAvatarPicker(null, null));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUpload() {
|
||||
|
||||
@@ -4,11 +4,14 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
interface EditProfileRepository {
|
||||
|
||||
void getCurrentAvatarColor(@NonNull Consumer<AvatarColor> avatarColorConsumer);
|
||||
|
||||
void getCurrentProfileName(@NonNull Consumer<ProfileName> profileNameConsumer);
|
||||
|
||||
void getCurrentAvatar(@NonNull Consumer<byte[]> avatarConsumer);
|
||||
|
||||
@@ -8,7 +8,9 @@ import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.profiles.edit.EditProfileRepository.UploadResult;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
@@ -29,10 +31,12 @@ class EditProfileViewModel extends ViewModel {
|
||||
private final MutableLiveData<byte[]> originalAvatar = new MutableLiveData<>();
|
||||
private final MutableLiveData<String> originalDisplayName = new MutableLiveData<>();
|
||||
private final SingleLiveEvent<UploadResult> uploadResult = new SingleLiveEvent<>();
|
||||
private final MutableLiveData<AvatarColor> avatarColor = new MutableLiveData<>();
|
||||
private final LiveData<Boolean> isFormValid;
|
||||
private final EditProfileRepository repository;
|
||||
private final GroupId groupId;
|
||||
private String originalDescription;
|
||||
private Media avatarMedia;
|
||||
|
||||
private EditProfileViewModel(@NonNull EditProfileRepository repository, boolean hasInstanceState, @Nullable GroupId groupId) {
|
||||
this.repository = repository;
|
||||
@@ -59,9 +63,15 @@ class EditProfileViewModel extends ViewModel {
|
||||
internalAvatar.setValue(value);
|
||||
originalAvatar.setValue(value);
|
||||
});
|
||||
|
||||
repository.getCurrentAvatarColor(avatarColor::setValue);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<AvatarColor> avatarColor() {
|
||||
return Transformations.distinctUntilChanged(avatarColor);
|
||||
}
|
||||
|
||||
public LiveData<String> givenName() {
|
||||
return Transformations.distinctUntilChanged(givenName);
|
||||
}
|
||||
@@ -90,6 +100,18 @@ class EditProfileViewModel extends ViewModel {
|
||||
return groupId != null;
|
||||
}
|
||||
|
||||
public @Nullable Media getAvatarMedia() {
|
||||
return avatarMedia;
|
||||
}
|
||||
|
||||
public void setAvatarMedia(@Nullable Media avatarMedia) {
|
||||
this.avatarMedia = avatarMedia;
|
||||
}
|
||||
|
||||
public @Nullable GroupId getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public boolean canRemoveProfilePhoto() {
|
||||
return hasAvatar();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.core.util.Consumer;
|
||||
|
||||
import org.signal.core.util.StreamUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob;
|
||||
@@ -42,6 +43,11 @@ public class EditSelfProfileRepository implements EditProfileRepository {
|
||||
this.excludeSystem = excludeSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCurrentAvatarColor(@NonNull Consumer<AvatarColor> avatarColorConsumer) {
|
||||
SimpleTask.run(() -> Recipient.self().getAvatarColor(), avatarColorConsumer::accept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCurrentProfileName(@NonNull Consumer<ProfileName> profileNameConsumer) {
|
||||
ProfileName storedProfileName = Recipient.self().getProfileName();
|
||||
|
||||
@@ -23,9 +23,9 @@ import com.bumptech.glide.Glide;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiUtil;
|
||||
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity;
|
||||
import org.thoughtcrime.securesms.mediasend.AvatarSelectionBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.profiles.manage.ManageProfileViewModel.AvatarState;
|
||||
@@ -35,8 +35,7 @@ import static android.app.Activity.RESULT_OK;
|
||||
|
||||
public class ManageProfileFragment extends LoggingFragment {
|
||||
|
||||
private static final String TAG = Log.tag(ManageProfileFragment.class);
|
||||
private static final short REQUEST_CODE_SELECT_AVATAR = 31726;
|
||||
private static final String TAG = Log.tag(ManageProfileFragment.class);
|
||||
|
||||
private Toolbar toolbar;
|
||||
private ImageView avatarView;
|
||||
@@ -86,22 +85,11 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
this.aboutContainer.setOnClickListener(v -> {
|
||||
Navigation.findNavController(v).navigate(ManageProfileFragmentDirections.actionManageAbout());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == REQUEST_CODE_SELECT_AVATAR && resultCode == RESULT_OK) {
|
||||
if (data != null && data.getBooleanExtra("delete", false)) {
|
||||
viewModel.onAvatarSelected(requireContext(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
Media result = data.getParcelableExtra(AvatarSelectionActivity.EXTRA_MEDIA);
|
||||
|
||||
getParentFragmentManager().setFragmentResultListener(AvatarPickerFragment.REQUEST_KEY_SELECT_AVATAR, getViewLifecycleOwner(), (key, bundle) -> {
|
||||
Media result = bundle.getParcelable(AvatarPickerFragment.SELECT_AVATAR_MEDIA);
|
||||
viewModel.onAvatarSelected(requireContext(), result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeViewModel() {
|
||||
@@ -193,10 +181,6 @@ public class ManageProfileFragment extends LoggingFragment {
|
||||
}
|
||||
|
||||
private void onAvatarClicked() {
|
||||
AvatarSelectionBottomSheetDialogFragment.create(viewModel.canRemoveAvatar(),
|
||||
true,
|
||||
REQUEST_CODE_SELECT_AVATAR,
|
||||
false)
|
||||
.show(getChildFragmentManager(), null);
|
||||
Navigation.findNavController(requireView()).navigate(ManageProfileFragmentDirections.actionManageProfileFragmentToAvatarPicker(null, null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto20dp;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
@@ -122,7 +123,7 @@ public class ReviewBannerView extends LinearLayout {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Drawable newFallbackDrawable(@NonNull Context context, int color, boolean inverted) {
|
||||
protected Drawable newFallbackDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
|
||||
return new FallbackPhoto20dp(getFallbackResId()).asDrawable(context, color, inverted);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user