Improve conversation open speed.
This commit is contained in:
parent
60874ba57b
commit
ec504af593
5 changed files with 65 additions and 17 deletions
|
@ -16,6 +16,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
@ -33,9 +34,9 @@ import java.util.Optional;
|
|||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* <p>
|
||||
* The workhorse of this repository is the ProgressEventHandler, which will supply a
|
||||
* 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 LiveData<Optional<VoiceNotePlayerView.State>> voiceNotePlayerViewState;
|
||||
private VoiceNoteProximityWakeLockManager voiceNoteProximityWakeLockManager;
|
||||
private boolean isMediaBrowserCreationPostponed;
|
||||
|
||||
private final MediaControllerCompatCallback mediaControllerCompatCallback = new MediaControllerCompatCallback();
|
||||
|
||||
public VoiceNoteMediaController(@NonNull FragmentActivity activity) {
|
||||
this.activity = activity;
|
||||
this.mediaBrowser = new MediaBrowserCompat(activity,
|
||||
new ComponentName(activity, VoiceNotePlaybackService.class),
|
||||
new ConnectionCallback(),
|
||||
null);
|
||||
this(activity, false);
|
||||
}
|
||||
|
||||
public VoiceNoteMediaController(@NonNull FragmentActivity activity, boolean postponeMediaBrowserCreation) {
|
||||
this.activity = activity;
|
||||
this.isMediaBrowserCreationPostponed = postponeMediaBrowserCreation;
|
||||
|
||||
activity.getLifecycle().addObserver(this);
|
||||
|
||||
|
@ -71,9 +74,9 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
VoiceNotePlaybackState.ClipType.Message message = (VoiceNotePlaybackState.ClipType.Message) playbackState.getClipType();
|
||||
LiveRecipient sender = Recipient.live(message.getSenderId());
|
||||
LiveRecipient threadRecipient = Recipient.live(message.getThreadRecipientId());
|
||||
LiveData<String> name = LiveDataUtil.combineLatest(sender.getLiveDataResolved(),
|
||||
threadRecipient.getLiveDataResolved(),
|
||||
(s, t) -> VoiceNoteMediaItemFactory.getTitle(activity, s, t, null));
|
||||
LiveData<String> name = LiveDataUtil.combineLatest(sender.getLiveDataResolved(),
|
||||
threadRecipient.getLiveDataResolved(),
|
||||
(s, t) -> VoiceNoteMediaItemFactory.getTitle(activity, s, t, null));
|
||||
|
||||
return Transformations.map(name, displayName -> Optional.of(
|
||||
new VoiceNotePlayerView.State(
|
||||
|
@ -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() {
|
||||
return voiceNotePlaybackState;
|
||||
}
|
||||
|
@ -103,8 +117,22 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
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
|
||||
public void onResume(@NonNull LifecycleOwner owner) {
|
||||
if (mediaBrowser == null && isMediaBrowserCreationPostponed) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureMediaBrowser();
|
||||
mediaBrowser.disconnect();
|
||||
mediaBrowser.connect();
|
||||
}
|
||||
|
@ -117,7 +145,9 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
MediaControllerCompat.getMediaController(activity).unregisterCallback(mediaControllerCompatCallback);
|
||||
}
|
||||
|
||||
mediaBrowser.disconnect();
|
||||
if (mediaBrowser != null) {
|
||||
mediaBrowser.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -201,8 +231,8 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
* Tells the Media service to resume playback of a given audio slide. If the audio slide is not
|
||||
* currently paused, playback will be started from the beginning.
|
||||
*
|
||||
* @param audioSlideUri The Uri of the desired audio slide
|
||||
* @param messageId The Message id of the given audio slide
|
||||
* @param audioSlideUri The Uri of the desired audio slide
|
||||
* @param messageId The Message id of the given audio slide
|
||||
*/
|
||||
public void resumePlayback(@NonNull Uri audioSlideUri, long messageId) {
|
||||
if (getMediaController() == null) {
|
||||
|
@ -390,8 +420,8 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
|
|||
}
|
||||
|
||||
private static boolean canExtractPlaybackInformationFromMetadata(@Nullable MediaMetadataCompat mediaMetadataCompat) {
|
||||
return mediaMetadataCompat != null &&
|
||||
mediaMetadataCompat.getDescription() != null &&
|
||||
return mediaMetadataCompat != null &&
|
||||
mediaMetadataCompat.getDescription() != null &&
|
||||
mediaMetadataCompat.getDescription().getMediaUri() != null;
|
||||
}
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ public class VoiceNotePlaybackService extends MediaBrowserServiceCompat {
|
|||
if (extras == null) {
|
||||
return;
|
||||
}
|
||||
long messageId = extras.getLong(VoiceNoteMediaItemFactory.EXTRA_MESSAGE_ID);
|
||||
long messageId = extras.getLong(VoiceNoteMediaItemFactory.EXTRA_MESSAGE_ID);
|
||||
RecipientId recipientId = RecipientId.from(extras.getString(VoiceNoteMediaItemFactory.EXTRA_INDIVIDUAL_RECIPIENT_ID));
|
||||
MessageTable messageDatabase = SignalDatabase.messages();
|
||||
|
||||
|
|
|
@ -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.StripeRepository
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.Debouncer
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme
|
||||
import org.thoughtcrime.securesms.util.views.Stub
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
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 val transitionDebouncer: Debouncer = Debouncer(150, TimeUnit.MILLISECONDS)
|
||||
private lateinit var fragment: ConversationParentFragment
|
||||
private var shareDataTimestamp: Long = -1L
|
||||
|
||||
|
@ -35,6 +38,8 @@ open class ConversationActivity : PassphraseRequiredActivity(), ConversationPare
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
supportPostponeEnterTransition()
|
||||
transitionDebouncer.publish { supportStartPostponedEnterTransition() }
|
||||
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
|
@ -51,6 +56,11 @@ open class ConversationActivity : PassphraseRequiredActivity(), ConversationPare
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
transitionDebouncer.clear()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLong(STATE_WATERMARK, shareDataTimestamp)
|
||||
|
|
|
@ -746,6 +746,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
startupStopwatch.split("first-render");
|
||||
startupStopwatch.stop(TAG);
|
||||
SignalLocalMetrics.ConversationOpen.onRenderFinished();
|
||||
listener.onFirstRender();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1480,6 +1481,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
@NonNull ConversationReactionOverlay.OnHideListener onHideListener);
|
||||
void onCursorChanged();
|
||||
void onMessageWithErrorClicked(@NonNull MessageRecord messageRecord);
|
||||
void onFirstRender();
|
||||
void onVoiceNotePause(@NonNull Uri uri);
|
||||
void onVoiceNotePlay(@NonNull Uri uri, long messageId, double progress);
|
||||
void onVoiceNoteResume(@NonNull Uri uri, long messageId);
|
||||
|
|
|
@ -512,7 +512,7 @@ public class ConversationParentFragment extends Fragment
|
|||
return;
|
||||
}
|
||||
|
||||
voiceNoteMediaController = new VoiceNoteMediaController(requireActivity());
|
||||
voiceNoteMediaController = new VoiceNoteMediaController(requireActivity(), true);
|
||||
voiceRecorderWakeLock = new VoiceRecorderWakeLock(requireActivity());
|
||||
|
||||
// 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
|
||||
public void onVoiceNotePause(@NonNull Uri uri) {
|
||||
voiceNoteMediaController.pausePlayback(uri);
|
||||
|
|
Loading…
Add table
Reference in a new issue