kopia lustrzana https://github.com/ryukoposting/Signal-Android
Made setting a profile photo a synchronous operation.
rodzic
5649c906a5
commit
9d5a52a980
|
@ -182,8 +182,13 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
}
|
||||
|
||||
private void presentEvent(@NonNull ManageProfileViewModel.Event event) {
|
||||
if (event == ManageProfileViewModel.Event.AVATAR_FAILURE) {
|
||||
Toast.makeText(requireContext(), R.string.ManageProfileFragment_failed_to_set_avatar, Toast.LENGTH_LONG).show();
|
||||
switch (event) {
|
||||
case AVATAR_DISK_FAILURE:
|
||||
Toast.makeText(requireContext(), R.string.ManageProfileFragment_failed_to_set_avatar, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case AVATAR_NETWORK_FAILURE:
|
||||
Toast.makeText(requireContext(), R.string.EditProfileNameFragment_failed_to_save_due_to_network_issues_try_again_later, Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,19 @@ package org.thoughtcrime.securesms.profiles.manage;
|
|||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.ProfileUtil;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
final class ManageProfileRepository {
|
||||
|
@ -36,6 +40,33 @@ final class ManageProfileRepository {
|
|||
try {
|
||||
ProfileUtil.uploadProfileWithAbout(context, about, emoji);
|
||||
DatabaseFactory.getRecipientDatabase(context).setAbout(Recipient.self().getId(), about, emoji);
|
||||
callback.accept(Result.SUCCESS);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to upload profile during about change.", e);
|
||||
callback.accept(Result.FAILURE_NETWORK);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setAvatar(@NonNull Context context, @NonNull byte[] data, @NonNull String contentType, @NonNull Consumer<Result> callback) {
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
ProfileUtil.uploadProfileWithAvatar(context, new StreamDetails(new ByteArrayInputStream(data), contentType, data.length));
|
||||
AvatarHelper.setAvatar(context, Recipient.self().getId(), new ByteArrayInputStream(data));
|
||||
callback.accept(Result.SUCCESS);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to upload profile during avatar change.", e);
|
||||
callback.accept(Result.FAILURE_NETWORK);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void clearAvatar(@NonNull Context context, @NonNull Consumer<Result> callback) {
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
ProfileUtil.uploadProfileWithAvatar(context, null);
|
||||
AvatarHelper.delete(context, Recipient.self().getId());
|
||||
|
||||
callback.accept(Result.SUCCESS);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to upload profile during name change.", e);
|
||||
|
|
|
@ -13,7 +13,6 @@ import org.signal.core.util.StreamUtil;
|
|||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
|
@ -25,7 +24,6 @@ import org.thoughtcrime.securesms.util.FeatureFlags;
|
|||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.whispersystems.signalservice.api.util.StreamDetails;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
|
@ -41,6 +39,9 @@ class ManageProfileViewModel extends ViewModel {
|
|||
private final MutableLiveData<String> aboutEmoji;
|
||||
private final SingleLiveEvent<Event> events;
|
||||
private final RecipientForeverObserver observer;
|
||||
private final ManageProfileRepository repository;
|
||||
|
||||
private byte[] previousAvatar;
|
||||
|
||||
public ManageProfileViewModel() {
|
||||
this.avatar = new MutableLiveData<>();
|
||||
|
@ -49,6 +50,7 @@ class ManageProfileViewModel extends ViewModel {
|
|||
this.about = new MutableLiveData<>();
|
||||
this.aboutEmoji = new MutableLiveData<>();
|
||||
this.events = new SingleLiveEvent<>();
|
||||
this.repository = new ManageProfileRepository();
|
||||
this.observer = this::onRecipientChanged;
|
||||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
|
@ -101,11 +103,21 @@ class ManageProfileViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
public void onAvatarSelected(@NonNull Context context, @Nullable Media media) {
|
||||
previousAvatar = avatar.getValue() != null ? avatar.getValue().getAvatar() : null;
|
||||
|
||||
if (media == null) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
AvatarHelper.delete(context, Recipient.self().getId());
|
||||
avatar.postValue(AvatarState.none());
|
||||
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
|
||||
avatar.postValue(AvatarState.loading(null));
|
||||
repository.clearAvatar(context, result -> {
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
avatar.postValue(AvatarState.loaded(null));
|
||||
previousAvatar = null;
|
||||
break;
|
||||
case FAILURE_NETWORK:
|
||||
avatar.postValue(AvatarState.loaded(previousAvatar));
|
||||
events.postValue(Event.AVATAR_NETWORK_FAILURE);
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
|
@ -113,13 +125,23 @@ class ManageProfileViewModel extends ViewModel {
|
|||
InputStream stream = BlobProvider.getInstance().getStream(context, media.getUri());
|
||||
byte[] data = StreamUtil.readFully(stream);
|
||||
|
||||
AvatarHelper.setAvatar(context, Recipient.self().getId(), new ByteArrayInputStream(data));
|
||||
avatar.postValue(AvatarState.loaded(data));
|
||||
avatar.postValue(AvatarState.loading(data));
|
||||
|
||||
ApplicationDependencies.getJobManager().add(new ProfileUploadJob());
|
||||
repository.setAvatar(context, data, media.getMimeType(), result -> {
|
||||
switch (result) {
|
||||
case SUCCESS:
|
||||
avatar.postValue(AvatarState.loaded(data));
|
||||
previousAvatar = data;
|
||||
break;
|
||||
case FAILURE_NETWORK:
|
||||
avatar.postValue(AvatarState.loaded(previousAvatar));
|
||||
events.postValue(Event.AVATAR_NETWORK_FAILURE);
|
||||
break;
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to save avatar!", e);
|
||||
events.postValue(Event.AVATAR_FAILURE);
|
||||
events.postValue(Event.AVATAR_DISK_FAILURE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -176,7 +198,7 @@ class ManageProfileViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
enum Event {
|
||||
AVATAR_FAILURE
|
||||
AVATAR_NETWORK_FAILURE, AVATAR_DISK_FAILURE
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
|
|
@ -107,10 +107,13 @@ public final class ProfileUtil {
|
|||
* successfully before persisting the change to disk.
|
||||
*/
|
||||
public static void uploadProfileWithName(@NonNull Context context, @NonNull ProfileName profileName) throws IOException {
|
||||
uploadProfile(context,
|
||||
profileName,
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""));
|
||||
try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
|
||||
uploadProfile(context,
|
||||
profileName,
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
|
||||
avatar);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,35 +122,51 @@ public final class ProfileUtil {
|
|||
* successfully before persisting the change to disk.
|
||||
*/
|
||||
public static void uploadProfileWithAbout(@NonNull Context context, @NonNull String about, @NonNull String emoji) throws IOException {
|
||||
uploadProfile(context,
|
||||
Recipient.self().getProfileName(),
|
||||
about,
|
||||
emoji);
|
||||
try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
|
||||
uploadProfile(context,
|
||||
Recipient.self().getProfileName(),
|
||||
about,
|
||||
emoji,
|
||||
avatar);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the profile based on all state that's written to disk, except we'll use the provided
|
||||
* avatar instead. This is useful when you want to ensure that the profile has been uploaded
|
||||
* successfully before persisting the change to disk.
|
||||
*/
|
||||
public static void uploadProfileWithAvatar(@NonNull Context context, @Nullable StreamDetails avatar) throws IOException {
|
||||
uploadProfile(context,
|
||||
Recipient.self().getProfileName(),
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
|
||||
avatar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the profile based on all state that's already written to disk.
|
||||
*/
|
||||
public static void uploadProfile(@NonNull Context context) throws IOException {
|
||||
uploadProfile(context,
|
||||
Recipient.self().getProfileName(),
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""));
|
||||
try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
|
||||
uploadProfile(context,
|
||||
Recipient.self().getProfileName(),
|
||||
Optional.fromNullable(Recipient.self().getAbout()).or(""),
|
||||
Optional.fromNullable(Recipient.self().getAboutEmoji()).or(""),
|
||||
avatar);
|
||||
}
|
||||
}
|
||||
|
||||
public static void uploadProfile(@NonNull Context context,
|
||||
@NonNull ProfileName profileName,
|
||||
@Nullable String about,
|
||||
@Nullable String aboutEmoji)
|
||||
private static void uploadProfile(@NonNull Context context,
|
||||
@NonNull ProfileName profileName,
|
||||
@Nullable String about,
|
||||
@Nullable String aboutEmoji,
|
||||
@Nullable StreamDetails avatar)
|
||||
throws IOException
|
||||
{
|
||||
ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey();
|
||||
String avatarPath;
|
||||
|
||||
try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
|
||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
avatarPath = accountManager.setVersionedProfile(Recipient.self().getUuid().get(), profileKey, profileName.serialize(), about, aboutEmoji, avatar).orNull();
|
||||
}
|
||||
ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey();
|
||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
String avatarPath = accountManager.setVersionedProfile(Recipient.self().getUuid().get(), profileKey, profileName.serialize(), about, aboutEmoji, avatar).orNull();
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileAvatar(Recipient.self().getId(), avatarPath);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue