Improve conversation open speed.

main
Alex Hart 2023-02-14 12:38:21 -04:00
rodzic 60874ba57b
commit ec504af593
5 zmienionych plików z 65 dodań i 17 usunięć

Wyświetl plik

@ -16,6 +16,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
@ -33,9 +34,9 @@ import java.util.Optional;
/** /**
* Encapsulates control of voice note playback from an Activity component. * Encapsulates control of voice note playback from an Activity component.
* * <p>
* This class assumes that it will be created within the scope of Activity#onCreate * This class assumes that it will be created within the scope of Activity#onCreate
* * <p>
* The workhorse of this repository is the ProgressEventHandler, which will supply a * The workhorse of this repository is the ProgressEventHandler, which will supply a
* steady stream of update events to the set callback. * steady stream of update events to the set callback.
*/ */
@ -54,15 +55,17 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
private MutableLiveData<VoiceNotePlaybackState> voiceNotePlaybackState = new MutableLiveData<>(VoiceNotePlaybackState.NONE); private MutableLiveData<VoiceNotePlaybackState> voiceNotePlaybackState = new MutableLiveData<>(VoiceNotePlaybackState.NONE);
private LiveData<Optional<VoiceNotePlayerView.State>> voiceNotePlayerViewState; private LiveData<Optional<VoiceNotePlayerView.State>> voiceNotePlayerViewState;
private VoiceNoteProximityWakeLockManager voiceNoteProximityWakeLockManager; private VoiceNoteProximityWakeLockManager voiceNoteProximityWakeLockManager;
private boolean isMediaBrowserCreationPostponed;
private final MediaControllerCompatCallback mediaControllerCompatCallback = new MediaControllerCompatCallback(); private final MediaControllerCompatCallback mediaControllerCompatCallback = new MediaControllerCompatCallback();
public VoiceNoteMediaController(@NonNull FragmentActivity activity) { public VoiceNoteMediaController(@NonNull FragmentActivity activity) {
this(activity, false);
}
public VoiceNoteMediaController(@NonNull FragmentActivity activity, boolean postponeMediaBrowserCreation) {
this.activity = activity; this.activity = activity;
this.mediaBrowser = new MediaBrowserCompat(activity, this.isMediaBrowserCreationPostponed = postponeMediaBrowserCreation;
new ComponentName(activity, VoiceNotePlaybackService.class),
new ConnectionCallback(),
null);
activity.getLifecycle().addObserver(this); activity.getLifecycle().addObserver(this);
@ -95,6 +98,17 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
}); });
} }
public void ensureMediaBrowser() {
if (mediaBrowser != null) {
return;
}
mediaBrowser = new MediaBrowserCompat(activity,
new ComponentName(activity, VoiceNotePlaybackService.class),
new ConnectionCallback(),
null);
}
public LiveData<VoiceNotePlaybackState> getVoiceNotePlaybackState() { public LiveData<VoiceNotePlaybackState> getVoiceNotePlaybackState() {
return voiceNotePlaybackState; return voiceNotePlaybackState;
} }
@ -103,8 +117,22 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
return voiceNotePlayerViewState; return voiceNotePlayerViewState;
} }
public void finishPostpone() {
isMediaBrowserCreationPostponed = false;
if (activity != null && mediaBrowser == null && activity.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
ensureMediaBrowser();
mediaBrowser.disconnect();
mediaBrowser.connect();
}
}
@Override @Override
public void onResume(@NonNull LifecycleOwner owner) { public void onResume(@NonNull LifecycleOwner owner) {
if (mediaBrowser == null && isMediaBrowserCreationPostponed) {
return;
}
ensureMediaBrowser();
mediaBrowser.disconnect(); mediaBrowser.disconnect();
mediaBrowser.connect(); mediaBrowser.connect();
} }
@ -117,8 +145,10 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
MediaControllerCompat.getMediaController(activity).unregisterCallback(mediaControllerCompatCallback); MediaControllerCompat.getMediaController(activity).unregisterCallback(mediaControllerCompatCallback);
} }
if (mediaBrowser != null) {
mediaBrowser.disconnect(); mediaBrowser.disconnect();
} }
}
@Override @Override
public void onDestroy(@NonNull LifecycleOwner owner) { public void onDestroy(@NonNull LifecycleOwner owner) {

Wyświetl plik

@ -16,9 +16,11 @@ import org.thoughtcrime.securesms.components.reminder.ReminderView
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentComponent import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentComponent
import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository import org.thoughtcrime.securesms.components.settings.app.subscription.StripeRepository
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
import org.thoughtcrime.securesms.util.DynamicTheme import org.thoughtcrime.securesms.util.DynamicTheme
import org.thoughtcrime.securesms.util.views.Stub import org.thoughtcrime.securesms.util.views.Stub
import java.util.concurrent.TimeUnit
open class ConversationActivity : PassphraseRequiredActivity(), ConversationParentFragment.Callback, DonationPaymentComponent { open class ConversationActivity : PassphraseRequiredActivity(), ConversationParentFragment.Callback, DonationPaymentComponent {
@ -26,6 +28,7 @@ open class ConversationActivity : PassphraseRequiredActivity(), ConversationPare
private const val STATE_WATERMARK = "share_data_watermark" private const val STATE_WATERMARK = "share_data_watermark"
} }
private val transitionDebouncer: Debouncer = Debouncer(150, TimeUnit.MILLISECONDS)
private lateinit var fragment: ConversationParentFragment private lateinit var fragment: ConversationParentFragment
private var shareDataTimestamp: Long = -1L private var shareDataTimestamp: Long = -1L
@ -35,6 +38,8 @@ open class ConversationActivity : PassphraseRequiredActivity(), ConversationPare
} }
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
supportPostponeEnterTransition()
transitionDebouncer.publish { supportStartPostponedEnterTransition() }
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
if (savedInstanceState != null) { if (savedInstanceState != null) {
@ -51,6 +56,11 @@ open class ConversationActivity : PassphraseRequiredActivity(), ConversationPare
} }
} }
override fun onDestroy() {
super.onDestroy()
transitionDebouncer.clear()
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
outState.putLong(STATE_WATERMARK, shareDataTimestamp) outState.putLong(STATE_WATERMARK, shareDataTimestamp)

Wyświetl plik

@ -746,6 +746,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
startupStopwatch.split("first-render"); startupStopwatch.split("first-render");
startupStopwatch.stop(TAG); startupStopwatch.stop(TAG);
SignalLocalMetrics.ConversationOpen.onRenderFinished(); SignalLocalMetrics.ConversationOpen.onRenderFinished();
listener.onFirstRender();
}); });
} }
}); });
@ -1480,6 +1481,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
@NonNull ConversationReactionOverlay.OnHideListener onHideListener); @NonNull ConversationReactionOverlay.OnHideListener onHideListener);
void onCursorChanged(); void onCursorChanged();
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord); void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
void onFirstRender();
void onVoiceNotePause(@NonNull Uri uri); void onVoiceNotePause(@NonNull Uri uri);
void onVoiceNotePlay(@NonNull Uri uri, long messageId, double progress); void onVoiceNotePlay(@NonNull Uri uri, long messageId, double progress);
void onVoiceNoteResume(@NonNull Uri uri, long messageId); void onVoiceNoteResume(@NonNull Uri uri, long messageId);

Wyświetl plik

@ -512,7 +512,7 @@ public class ConversationParentFragment extends Fragment
return; return;
} }
voiceNoteMediaController = new VoiceNoteMediaController(requireActivity()); voiceNoteMediaController = new VoiceNoteMediaController(requireActivity(), true);
voiceRecorderWakeLock = new VoiceRecorderWakeLock(requireActivity()); voiceRecorderWakeLock = new VoiceRecorderWakeLock(requireActivity());
// TODO [alex] LargeScreenSupport -- Should be removed once we move to multi-pane layout. // TODO [alex] LargeScreenSupport -- Should be removed once we move to multi-pane layout.
@ -4040,6 +4040,12 @@ public class ConversationParentFragment extends Fragment
} }
} }
@Override
public void onFirstRender() {
requireActivity().supportStartPostponedEnterTransition();
voiceNoteMediaController.finishPostpone();
}
@Override @Override
public void onVoiceNotePause(@NonNull Uri uri) { public void onVoiceNotePause(@NonNull Uri uri) {
voiceNoteMediaController.pausePlayback(uri); voiceNoteMediaController.pausePlayback(uri);