Migrate all internal shares to MultiselectForwardFragment.

This commit is contained in:
Alex Hart
2022-04-12 09:33:28 -03:00
committed by Greyson Parrelli
parent 23939aeee3
commit 03e8fe9f27
10 changed files with 119 additions and 101 deletions

View File

@@ -44,7 +44,7 @@ class MultiselectForwardBottomSheet : FixedRoundedCornerBottomSheetDialogFragmen
}
override fun setResult(bundle: Bundle) {
setFragmentResult(MultiselectForwardFragment.RESULT_SELECTION, bundle)
setFragmentResult(MultiselectForwardFragment.RESULT_KEY, bundle)
}
override fun onDismiss(dialog: DialogInterface) {

View File

@@ -44,9 +44,24 @@ import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.fragments.findListener
import org.thoughtcrime.securesms.util.fragments.requireListener
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog
import org.thoughtcrime.securesms.util.visible
/**
* Allows selection and optional sending to one or more users.
*
* This fragment is designed to be displayed in a Dialog fragment, and thus has two available constructors to display as a bottom sheet or full screen dialog.
*
* To customize the available recipients, a parent must implement `SearchConfigurationProvider`
*
* This fragment will emit one of two possible result values at the same key, `RESULT_KEY`:
*
* - If the arguments contain a non-empty list of MultiShareArgs, then messages will be sent when the selection is confirmed. This will result in `RESULT_SENT` being true.
* - If the arguments contain an empty list of MultiShareArgs, then the selection will be returned on confirmation. This will result in `RESULT_SELECTION` being set.
*
* It is up to the user of this fragment to handle the result accordingly utilizing a fragment result listener.
*/
class MultiselectForwardFragment :
Fragment(R.layout.multiselect_forward_fragment),
SafetyNumberChangeDialog.Callback,
@@ -98,8 +113,8 @@ class MultiselectForwardFragment :
contactSearchMediator.onFilterChanged(it)
}
val title: TextView? = view.findViewById(R.id.title)
val container = callback.getContainer()
val title: TextView? = container.findViewById(R.id.title)
val bottomBar = LayoutInflater.from(requireContext()).inflate(R.layout.multiselect_forward_fragment_bottom_bar, container, false)
val shareSelectionRecycler: RecyclerView = bottomBar.findViewById(R.id.selected_list)
val shareSelectionAdapter = ShareSelectionAdapter()
@@ -162,9 +177,9 @@ class MultiselectForwardFragment :
dismissibleDialog?.dismiss()
dismissibleDialog = SimpleProgressDialog.showDelayed(requireContext())
}
MultiselectForwardState.Stage.SomeFailed -> dismissAndShowToast(R.plurals.MultiselectForwardFragment_messages_sent)
MultiselectForwardState.Stage.SomeFailed -> dismissWithSuccess(R.plurals.MultiselectForwardFragment_messages_sent)
MultiselectForwardState.Stage.AllFailed -> dismissAndShowToast(R.plurals.MultiselectForwardFragment_messages_failed_to_send)
MultiselectForwardState.Stage.Success -> dismissAndShowToast(R.plurals.MultiselectForwardFragment_messages_sent)
MultiselectForwardState.Stage.Success -> dismissWithSuccess(R.plurals.MultiselectForwardFragment_messages_sent)
is MultiselectForwardState.Stage.SelectionConfirmed -> dismissWithSelection(it.stage.selectedContacts)
}
@@ -242,6 +257,16 @@ class MultiselectForwardFragment :
SafetyNumberChangeDialog.show(childFragmentManager, identityRecords)
}
private fun dismissWithSuccess(@PluralsRes toastTextResId: Int) {
requireListener<Callback>().setResult(
Bundle().apply {
putBoolean(RESULT_SENT, true)
}
)
dismissAndShowToast(toastTextResId)
}
private fun dismissAndShowToast(@PluralsRes toastTextResId: Int) {
val argCount = getMessageCount()
@@ -265,7 +290,7 @@ class MultiselectForwardFragment :
dismissibleDialog?.dismiss()
val resultsBundle = Bundle().apply {
putParcelableArrayList(RESULT_SELECTION_RECIPIENTS, ArrayList(selectedContacts.map { it.requireParcelable() }))
putParcelableArrayList(RESULT_SELECTION, ArrayList(selectedContacts.map { it.requireParcelable() }))
}
callback.setResult(resultsBundle)
@@ -367,8 +392,9 @@ class MultiselectForwardFragment :
const val ARG_MULTISHARE_ARGS = "multiselect.forward.fragment.arg.multishare.args"
const val ARG_CAN_SEND_TO_NON_PUSH = "multiselect.forward.fragment.arg.can.send.to.non.push"
const val ARG_TITLE = "multiselect.forward.fragment.title"
const val RESULT_SELECTION = "result_selection"
const val RESULT_SELECTION_RECIPIENTS = "result_selection_recipients"
const val RESULT_KEY = "result_key"
const val RESULT_SELECTION = "result_selection_recipients"
const val RESULT_SENT = "result_sent"
@JvmStatic
fun showBottomSheet(supportFragmentManager: FragmentManager, multiselectForwardFragmentArgs: MultiselectForwardFragmentArgs) {

View File

@@ -10,7 +10,7 @@ import org.thoughtcrime.securesms.components.FullScreenDialogFragment
import org.thoughtcrime.securesms.util.fragments.findListener
class MultiselectForwardFullScreenDialogFragment : FullScreenDialogFragment(), MultiselectForwardFragment.Callback {
override fun getTitle(): Int = R.string.MediaReviewFragment__send_to
override fun getTitle(): Int = requireArguments().getInt(MultiselectForwardFragment.ARG_TITLE)
override fun getDialogLayoutResource(): Int = R.layout.fragment_container
@@ -38,7 +38,7 @@ class MultiselectForwardFullScreenDialogFragment : FullScreenDialogFragment(), M
}
override fun setResult(bundle: Bundle) {
setFragmentResult(MultiselectForwardFragment.RESULT_SELECTION, bundle)
setFragmentResult(MultiselectForwardFragment.RESULT_KEY, bundle)
}
override fun exitFlow() {

View File

@@ -139,8 +139,8 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment) {
sharedViewModel.sendCommand(HudCommand.SaveMedia)
}
setFragmentResultListener(MultiselectForwardFragment.RESULT_SELECTION) { _, bundle ->
val parcelizedKeys: List<ContactSearchKey.ParcelableContactSearchKey> = bundle.getParcelableArrayList(MultiselectForwardFragment.RESULT_SELECTION_RECIPIENTS)!!
setFragmentResultListener(MultiselectForwardFragment.RESULT_KEY) { _, bundle ->
val parcelizedKeys: List<ContactSearchKey.ParcelableContactSearchKey> = bundle.getParcelableArrayList(MultiselectForwardFragment.RESULT_SELECTION)!!
val contactSearchKeys = parcelizedKeys.map { it.asContactSearchKey() }
performSend(contactSearchKeys)
}

View File

@@ -1,7 +1,6 @@
package org.thoughtcrime.securesms.recipients.ui.sharablegrouplink;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -18,14 +17,17 @@ import androidx.fragment.app.FragmentManager;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.groups.LiveGroup;
import org.thoughtcrime.securesms.recipients.ui.sharablegrouplink.qr.GroupLinkShareQrDialogFragment;
import org.thoughtcrime.securesms.sharing.ShareActivity;
import org.thoughtcrime.securesms.sharing.MultiShareArgs;
import org.thoughtcrime.securesms.util.BottomSheetUtil;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
import java.util.Collections;
import java.util.Objects;
public final class GroupLinkBottomSheetDialogFragment extends BottomSheetDialogFragment {
@@ -77,10 +79,16 @@ public final class GroupLinkBottomSheetDialogFragment extends BottomSheetDialogF
hint.setVisibility(View.VISIBLE);
shareViaSignalButton.setOnClickListener(v -> {
Context context = requireContext();
Intent intent = new Intent(context, ShareActivity.class);
intent.putExtra(Intent.EXTRA_TEXT, groupLink.getUrl());
context.startActivity(intent);
MultiselectForwardFragment.showBottomSheet(
getParentFragmentManager(),
new MultiselectForwardFragmentArgs(
true,
Collections.singletonList(new MultiShareArgs.Builder(Collections.emptySet())
.withDraftText(groupLink.getUrl())
.build()),
R.string.MultiselectForwardFragment__share_with
)
);
dismiss();
});

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.shakereport;
import android.app.Activity;
import android.app.Application;
import android.widget.TextView;
import android.widget.Toast;
@@ -16,14 +15,17 @@ import org.signal.core.util.ShakeDetector;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository;
import org.thoughtcrime.securesms.sharing.ShareIntents;
import org.thoughtcrime.securesms.sharing.MultiShareArgs;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import java.lang.ref.WeakReference;
import java.util.Collections;
/**
* A class that will detect a shake and then prompts the user to submit a debuglog. Basically a
@@ -90,7 +92,7 @@ public final class ShakeToReport implements ShakeDetector.Listener {
}
}
private void submitLog(@NonNull Activity activity) {
private void submitLog(@NonNull AppCompatActivity activity) {
AlertDialog spinner = SimpleProgressDialog.show(activity);
SubmitDebugLogRepository repo = new SubmitDebugLogRepository();
@@ -112,7 +114,7 @@ public final class ShakeToReport implements ShakeDetector.Listener {
});
}
private void showPostSubmitDialog(@NonNull Activity activity, @NonNull String url) {
private void showPostSubmitDialog(@NonNull AppCompatActivity activity, @NonNull String url) {
AlertDialog dialog = new MaterialAlertDialogBuilder(activity)
.setTitle(R.string.ShakeToReport_success)
.setMessage(url)
@@ -124,9 +126,16 @@ public final class ShakeToReport implements ShakeDetector.Listener {
d.dismiss();
enableIfVisible();
activity.startActivity(new ShareIntents.Builder(activity)
.setText(url)
.build());
MultiselectForwardFragment.showFullScreen(
activity.getSupportFragmentManager(),
new MultiselectForwardFragmentArgs(
true,
Collections.singletonList(new MultiShareArgs.Builder(Collections.emptySet())
.withDraftText(url)
.build()),
R.string.MultiselectForwardFragment__share_with
)
);
})
.show();

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.sharing;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
@@ -8,14 +7,11 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.stickers.StickerLocator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public final class ShareIntents {
final class ShareIntents {
private static final String EXTRA_MEDIA = "extra_media";
private static final String EXTRA_BORDERLESS = "extra_borderless";
@@ -71,58 +67,4 @@ public final class ShareIntents {
TextUtils.isEmpty(extraText);
}
}
public static final class Builder {
private final Context context;
private String extraText;
private List<Media> extraMedia;
private Slide slide;
public Builder(@NonNull Context context) {
this.context = context;
}
public @NonNull Builder setText(@NonNull CharSequence extraText) {
this.extraText = extraText.toString();
return this;
}
public @NonNull Builder setMedia(@NonNull Collection<Media> extraMedia) {
this.extraMedia = new ArrayList<>(extraMedia);
return this;
}
public @NonNull Builder setSlide(@NonNull Slide slide) {
this.slide = slide;
return this;
}
public @NonNull Intent build() {
if (slide != null && extraMedia != null) {
throw new IllegalStateException("Cannot create intent with both Slide and [Media]");
}
Intent intent = new Intent(context, ShareActivity.class);
intent.putExtra(Intent.EXTRA_TEXT, extraText);
if (extraMedia != null) {
intent.putParcelableArrayListExtra(EXTRA_MEDIA, new ArrayList<>(extraMedia));
} else if (slide != null) {
intent.putExtra(Intent.EXTRA_STREAM, slide.getUri());
intent.putExtra(EXTRA_BORDERLESS, slide.isBorderless());
if (slide.hasSticker()) {
intent.putExtra(EXTRA_STICKER, slide.asAttachment().getSticker());
intent.setType(slide.asAttachment().getContentType());
} else {
intent.setType(slide.getContentType());
}
}
return intent;
}
}
}

View File

@@ -13,11 +13,15 @@ import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.sharing.ShareActivity;
import org.thoughtcrime.securesms.sharing.MultiShareArgs;
import org.thoughtcrime.securesms.util.DeviceProperties;
import org.thoughtcrime.securesms.util.DynamicTheme;
import java.util.Collections;
/**
* Allows the user to view and manage (install, uninstall, etc) their stickers.
*/
@@ -46,6 +50,12 @@ public final class StickerManagementActivity extends PassphraseRequiredActivity
initView();
initToolbar();
initViewModel();
getSupportFragmentManager().setFragmentResultListener(MultiselectForwardFragment.RESULT_KEY, this, (requestKey, result) -> {
if (result.getBoolean(MultiselectForwardFragment.RESULT_SENT, false)) {
finish();
}
});
}
@Override
@@ -86,10 +96,16 @@ public final class StickerManagementActivity extends PassphraseRequiredActivity
@Override
public void onStickerPackShareClicked(@NonNull String packId, @NonNull String packKey) {
Intent composeIntent = new Intent(this, ShareActivity.class);
composeIntent.putExtra(Intent.EXTRA_TEXT, StickerUrl.createShareLink(packId, packKey));
startActivity(composeIntent);
finish();
MultiselectForwardFragment.showBottomSheet(
getSupportFragmentManager(),
new MultiselectForwardFragmentArgs(
true,
Collections.singletonList(new MultiShareArgs.Builder(Collections.emptySet())
.withDraftText(StickerUrl.createShareLink(packId, packKey))
.build()),
R.string.MultiselectForwardFragment__share_with
)
);
}
private void initView() {

View File

@@ -22,16 +22,19 @@ import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.util.Pair;
import org.thoughtcrime.securesms.PassphraseRequiredActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment;
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs;
import org.thoughtcrime.securesms.glide.cache.ApngOptions;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.sharing.ShareActivity;
import org.thoughtcrime.securesms.sharing.MultiShareArgs;
import org.thoughtcrime.securesms.stickers.StickerManifest.Sticker;
import org.thoughtcrime.securesms.util.DeviceProperties;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.whispersystems.signalservice.api.util.OptionalUtil;
import java.util.Collections;
import java.util.Optional;
@@ -40,9 +43,9 @@ import java.util.Optional;
* (if installed). This is also the handler for sticker pack deep links.
*/
public final class StickerPackPreviewActivity extends PassphraseRequiredActivity
implements StickerRolloverTouchListener.RolloverEventListener,
StickerRolloverTouchListener.RolloverStickerRetriever,
StickerPackPreviewAdapter.EventListener
implements StickerRolloverTouchListener.RolloverEventListener,
StickerRolloverTouchListener.RolloverStickerRetriever,
StickerPackPreviewAdapter.EventListener
{
private static final String TAG = Log.tag(StickerPackPreviewActivity.class);
@@ -95,6 +98,12 @@ public final class StickerPackPreviewActivity extends PassphraseRequiredActivity
initToolbar();
initView();
initViewModel(packId, packKey);
getSupportFragmentManager().setFragmentResultListener(MultiselectForwardFragment.RESULT_KEY, this, (requestKey, result) -> {
if (result.getBoolean(MultiselectForwardFragment.RESULT_SENT, false)) {
finish();
}
});
}
@Override
@@ -193,12 +202,12 @@ public final class StickerPackPreviewActivity extends PassphraseRequiredActivity
Sticker cover = OptionalUtil.or(manifest.getCover(), Optional.ofNullable(first)).orElse(null);
if (cover != null) {
Object model = cover.getUri().isPresent() ? new DecryptableStreamUriLoader.DecryptableUri(cover.getUri().get())
: new StickerRemoteUri(cover.getPackId(), cover.getPackKey(), cover.getId());
Object model = cover.getUri().isPresent() ? new DecryptableStreamUriLoader.DecryptableUri(cover.getUri().get())
: new StickerRemoteUri(cover.getPackId(), cover.getPackKey(), cover.getId());
GlideApp.with(this).load(model)
.transition(DrawableTransitionOptions.withCrossFade())
.set(ApngOptions.ANIMATE, DeviceProperties.shouldAllowApngStickerAnimation(this))
.into(coverImage);
.transition(DrawableTransitionOptions.withCrossFade())
.set(ApngOptions.ANIMATE, DeviceProperties.shouldAllowApngStickerAnimation(this))
.into(coverImage);
} else {
coverImage.setImageDrawable(null);
}
@@ -229,10 +238,16 @@ public final class StickerPackPreviewActivity extends PassphraseRequiredActivity
shareButton.setVisibility(View.VISIBLE);
shareButtonImage.setVisibility(View.VISIBLE);
shareButton.setOnClickListener(v -> {
Intent composeIntent = new Intent(this, ShareActivity.class);
composeIntent.putExtra(Intent.EXTRA_TEXT, StickerUrl.createShareLink(packId, packKey));
startActivity(composeIntent);
finish();
MultiselectForwardFragment.showBottomSheet(
getSupportFragmentManager(),
new MultiselectForwardFragmentArgs(
true,
Collections.singletonList(new MultiShareArgs.Builder(Collections.emptySet())
.withDraftText(StickerUrl.createShareLink(packId, packKey))
.build()),
R.string.MultiselectForwardFragment__share_with
)
);
});
} else {
shareButton.setVisibility(View.GONE);

View File

@@ -4021,6 +4021,8 @@
<!-- DSLSettingsToolbar -->
<string name="DSLSettingsToolbar__navigate_up">Navigate up</string>
<string name="MultiselectForwardFragment__forward_to">Forward to</string>
<!-- Displayed when sharing content via the fragment -->
<string name="MultiselectForwardFragment__share_with">Share with</string>
<string name="MultiselectForwardFragment__add_a_message">Add a message</string>
<string name="MultiselectForwardFragment__faster_forwards">Faster forwards</string>
<string name="MultiselectForwardFragment__forwarded_messages_are_now">Forwarded messages are now sent immediately.</string>