Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix racey dialog dismissal in PlacePickerActivity #13461

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -185,7 +185,7 @@ class ConversationSettingsFragment : DSLSettingsFragment(
when (requestCode) {
REQUEST_CODE_ADD_MEMBERS_TO_GROUP -> if (data != null) {
val selected: List<RecipientId> = requireNotNull(data.getParcelableArrayListExtraCompat(PushContactSelectionActivity.KEY_SELECTED_RECIPIENTS, RecipientId::class.java))
val progress: SimpleProgressDialog.DismissibleDialog = SimpleProgressDialog.showDelayed(requireContext())
val progress: SimpleProgressDialog.DismissibleDialog = SimpleProgressDialog.showDelayed(requireContext(), this)
fynngodau marked this conversation as resolved.
Show resolved Hide resolved

viewModel.onAddToGroupComplete(selected) {
progress.dismiss()
Expand Down
Expand Up @@ -249,7 +249,7 @@ class MultiselectForwardFragment :
MultiselectForwardState.Stage.SendPending -> {
handler?.removeCallbacksAndMessages(null)
dismissibleDialog?.dismiss()
dismissibleDialog = SimpleProgressDialog.showDelayed(requireContext())
dismissibleDialog = SimpleProgressDialog.showDelayed(requireContext(), this)
fynngodau marked this conversation as resolved.
Show resolved Hide resolved
}
MultiselectForwardState.Stage.SomeFailed -> dismissWithSuccess(R.plurals.MultiselectForwardFragment_messages_sent)
MultiselectForwardState.Stage.AllFailed -> dismissAndShowToast(R.plurals.MultiselectForwardFragment_messages_failed_to_send)
Expand Down
Expand Up @@ -1279,7 +1279,7 @@ private void handleUnmute(@NonNull Collection<Conversation> conversations) {
}

private void updateMute(@NonNull Collection<Conversation> conversations, long until) {
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(requireContext(), 250, 250);
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(requireContext(), this, 250, 250);
fynngodau marked this conversation as resolved.
Show resolved Hide resolved

SimpleTask.run(SignalExecutors.BOUNDED, () -> {
List<RecipientId> recipientIds = conversations.stream()
Expand Down
Expand Up @@ -164,7 +164,7 @@ private void shrinkSkip() {

private void handleNextPressed() {
Stopwatch stopwatch = new Stopwatch("Recipient Refresh");
SimpleProgressDialog.DismissibleDialog dismissibleDialog = SimpleProgressDialog.showDelayed(this);
SimpleProgressDialog.DismissibleDialog dismissibleDialog = SimpleProgressDialog.showDelayed(this, this);

SimpleTask.run(getLifecycle(), () -> {
List<RecipientId> ids = contactsFragment.getSelectedContacts()
Expand Down
Expand Up @@ -154,7 +154,7 @@ protected GroupId.V2 getGroupId() {
private void setBusy(boolean isBusy) {
if (isBusy) {
if (busyDialog == null) {
busyDialog = SimpleProgressDialog.showDelayed(requireContext());
busyDialog = SimpleProgressDialog.showDelayed(requireContext(), this);
fynngodau marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
if (busyDialog != null) {
Expand Down
Expand Up @@ -74,7 +74,7 @@ private void display() {
}

private void onAddClicked(@NonNull DialogInterface rootDialog) {
SimpleProgressDialog.DismissibleDialog progressDialog = SimpleProgressDialog.showDelayed(fragmentActivity, 300, 0);
SimpleProgressDialog.DismissibleDialog progressDialog = SimpleProgressDialog.showDelayed(fragmentActivity, fragmentActivity, 300, 0);
SimpleTask.run(SignalExecutors.UNBOUNDED, () -> {
try {
GroupManager.addMembers(fragmentActivity, groupId.requirePush(), suggestions);
Expand Down
Expand Up @@ -190,12 +190,11 @@ private void finishWithAddress() {
String address = currentAddress != null && currentAddress.getAddressLine(0) != null ? currentAddress.getAddressLine(0) : "";
AddressData addressData = new AddressData(currentLocation.latitude, currentLocation.longitude, address);

SimpleProgressDialog.DismissibleDialog dismissibleDialog = SimpleProgressDialog.showDelayed(this);
MapView mapView = findViewById(R.id.map_view);
SimpleProgressDialog.DismissibleDialog dismissibleDialog = SimpleProgressDialog.showDelayed(this, this);
MapView mapView = findViewById(R.id.map_view);
SignalMapView.snapshot(currentLocation, mapView).addListener(new ListenableFuture.Listener<>() {
@Override
public void onSuccess(Bitmap result) {
dismissibleDialog.dismiss();
byte[] blob = BitmapUtil.toByteArray(result);
Uri uri = BlobProvider.getInstance()
.forData(blob)
Expand Down
Expand Up @@ -89,7 +89,7 @@ public boolean onOptionsItemSelected(MenuItem item) {
}

private void handleToken(@NonNull String token) {
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(this, 1000, 500);
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(this, this, 1000, 500);
SimpleTask.run(() -> {
String challenge = SignalStore.rateLimit().getChallenge();
if (Util.isEmpty(challenge)) {
Expand Down
Expand Up @@ -50,7 +50,7 @@ class ShareableGroupLinkFragment : DSLSettingsFragment(
{ busy ->
if (busy) {
if (busyDialog == null) {
busyDialog = SimpleProgressDialog.showDelayed(requireContext())
busyDialog = SimpleProgressDialog.showDelayed(requireContext(), this)
fynngodau marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
busyDialog?.dismiss()
Expand Down
Expand Up @@ -431,8 +431,8 @@ private static void startVideoCallInternal(@NonNull CallContext callContext, @No
.execute();
}

private static void handleE164Link(Activity activity, String e164) {
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(activity, 500, 500);
private static void handleE164Link(FragmentActivity activity, String e164) {
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(activity, activity, 500, 500);

SimpleTask.run(() -> {
Recipient recipient = Recipient.external(activity, e164);
Expand Down Expand Up @@ -461,8 +461,8 @@ private static void handleE164Link(Activity activity, String e164) {
});
}

private static void handleUsernameLink(Activity activity, String link) {
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(activity, 500, 500);
private static void handleUsernameLink(FragmentActivity activity, String link) {
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(activity, activity, 500, 500);

SimpleTask.run(() -> {
try {
Expand Down
Expand Up @@ -6,6 +6,9 @@
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;

import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
Expand Down Expand Up @@ -37,8 +40,10 @@ private SimpleProgressDialog() {}
}

@AnyThread
public static @NonNull DismissibleDialog showDelayed(@NonNull Context context) {
return showDelayed(context, 300, 1000);
public static @NonNull DismissibleDialog showDelayed(@NonNull Context context,
@NonNull LifecycleOwner lifecycleOwner)
{
return showDelayed(context, lifecycleOwner, 300, 1000);
}

/**
Expand All @@ -54,51 +59,59 @@ private SimpleProgressDialog() {}
*/
@AnyThread
public static @NonNull DismissibleDialog showDelayed(@NonNull Context context,
@NonNull LifecycleOwner lifecycleOwner,
int delayMs,
int minimumShowTimeMs)
{
AtomicReference<AlertDialog> dialogAtomicReference = new AtomicReference<>();
AtomicLong shownAt = new AtomicLong();

Runnable showRunnable = () -> {
Log.i(TAG, "Taking some time. Showing a progress dialog.");
shownAt.set(System.currentTimeMillis());
dialogAtomicReference.set(show(context));
};
final AtomicReference<Runnable> showRunnable = new AtomicReference<>(null);

ThreadUtil.runOnMainDelayed(showRunnable, delayMs);
final Runnable tryDismiss = () -> {
AlertDialog alertDialog = dialogAtomicReference.getAndSet(null);
if (alertDialog != null) {
alertDialog.dismiss();
}
};

return new DismissibleDialog() {
DismissibleDialog dialog = new DismissibleDialog() {
@Override
public void dismiss() {
ThreadUtil.cancelRunnableOnMain(showRunnable);
ThreadUtil.runOnMain(() -> {
AlertDialog alertDialog = dialogAtomicReference.getAndSet(null);
if (alertDialog != null) {
long beenShowingForMs = System.currentTimeMillis() - shownAt.get();
long remainingTimeMs = minimumShowTimeMs - beenShowingForMs;

if (remainingTimeMs > 0) {
ThreadUtil.runOnMainDelayed(alertDialog::dismiss, remainingTimeMs);
} else {
alertDialog.dismiss();
}
}
});
ThreadUtil.cancelRunnableOnMain(showRunnable.get());

long beenShowingForMs = System.currentTimeMillis() - shownAt.get();
long remainingTimeMs = minimumShowTimeMs + 1000 - beenShowingForMs;
long timeUntilDismiss = Math.max(remainingTimeMs, 0);

ThreadUtil.runOnMainDelayed(tryDismiss, timeUntilDismiss);
}

@Override
public void dismissNow() {
ThreadUtil.cancelRunnableOnMain(showRunnable);
ThreadUtil.runOnMain(() -> {
AlertDialog alertDialog = dialogAtomicReference.getAndSet(null);
if (alertDialog != null) {
alertDialog.dismiss();
}
});
ThreadUtil.cancelRunnableOnMain(showRunnable.get());
ThreadUtil.runOnMain(tryDismiss);
}
};
}

showRunnable.set(() -> {
Log.i(TAG, "Taking some time. Showing a progress dialog.");
shownAt.set(System.currentTimeMillis());
dialogAtomicReference.set(show(context));

LifecycleObserver observer = new DefaultLifecycleObserver() {
@Override public void onStop(@NonNull LifecycleOwner owner) {
// avoid leaking the dialog when its parent activity is destroyed
tryDismiss.run();
}
};
lifecycleOwner.getLifecycle().addObserver(observer);
});

ThreadUtil.runOnMainDelayed(showRunnable.get(), delayMs);

return dialog;
};

public interface DismissibleDialog {
@AnyThread
Expand Down