Add animations to call screen.
parent
0c73ddc08b
commit
e05f137bd8
|
@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.events.CallParticipant;
|
|||
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
||||
import org.thoughtcrime.securesms.ringrtc.CameraState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -17,7 +18,9 @@ import java.util.List;
|
|||
*/
|
||||
public final class CallParticipantsState {
|
||||
|
||||
public static final CallParticipantsState STARTING_STATE = new CallParticipantsState(WebRtcViewModel.State.CALL_DISCONNECTED,
|
||||
private static final int SMALL_GROUP_MAX = 6;
|
||||
|
||||
public static final CallParticipantsState STARTING_STATE = new CallParticipantsState(WebRtcViewModel.State.CALL_DISCONNECTED,
|
||||
Collections.emptyList(),
|
||||
CallParticipant.createLocal(CameraState.UNKNOWN, new BroadcastVideoSink(null), false),
|
||||
null,
|
||||
|
@ -55,21 +58,29 @@ public final class CallParticipantsState {
|
|||
}
|
||||
|
||||
public @NonNull List<CallParticipant> getGridParticipants() {
|
||||
if (getAllRemoteParticipants().size() > 6) {
|
||||
return getAllRemoteParticipants().subList(0, 6);
|
||||
if (getAllRemoteParticipants().size() > SMALL_GROUP_MAX) {
|
||||
return getAllRemoteParticipants().subList(0, SMALL_GROUP_MAX);
|
||||
} else {
|
||||
return getAllRemoteParticipants();
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull List<CallParticipant> getListParticipants() {
|
||||
List<CallParticipant> listParticipants = new ArrayList<>();
|
||||
|
||||
if (isViewingFocusedParticipant && getAllRemoteParticipants().size() > 1) {
|
||||
return getAllRemoteParticipants().subList(1, getAllRemoteParticipants().size());
|
||||
} else if (getAllRemoteParticipants().size() > 6) {
|
||||
return getAllRemoteParticipants().subList(6, getAllRemoteParticipants().size());
|
||||
listParticipants.addAll(getAllRemoteParticipants().subList(1, getAllRemoteParticipants().size()));
|
||||
} else if (getAllRemoteParticipants().size() > SMALL_GROUP_MAX) {
|
||||
listParticipants.addAll(getAllRemoteParticipants().subList(SMALL_GROUP_MAX, getAllRemoteParticipants().size()));
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
listParticipants.add(CallParticipant.EMPTY);
|
||||
|
||||
Collections.reverse(listParticipants);
|
||||
|
||||
return listParticipants;
|
||||
}
|
||||
|
||||
public @NonNull List<CallParticipant> getAllRemoteParticipants() {
|
||||
|
@ -88,6 +99,10 @@ public final class CallParticipantsState {
|
|||
return localRenderState;
|
||||
}
|
||||
|
||||
public boolean isLargeVideoGroup() {
|
||||
return getAllRemoteParticipants().size() > SMALL_GROUP_MAX;
|
||||
}
|
||||
|
||||
public boolean isInPipMode() {
|
||||
return isInPipMode;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.view.VelocityTracker;
|
|||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -16,7 +17,6 @@ import androidx.core.view.GestureDetectorCompat;
|
|||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -26,11 +26,14 @@ import java.util.Queue;
|
|||
|
||||
public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestureListener {
|
||||
|
||||
private static final float DECELERATION_RATE = 0.99f;
|
||||
private static final float DECELERATION_RATE = 0.99f;
|
||||
private static final Interpolator FLING_INTERPOLATOR = new ViscousFluidInterpolator();
|
||||
private static final Interpolator ADJUST_INTERPOLATOR = new AccelerateDecelerateInterpolator();
|
||||
|
||||
private final ViewGroup parent;
|
||||
private final View child;
|
||||
private final int framePadding;
|
||||
private final ViewGroup parent;
|
||||
private final View child;
|
||||
private final int framePadding;
|
||||
private final Queue<Runnable> runAfterFling;
|
||||
|
||||
private int pipWidth;
|
||||
private int pipHeight;
|
||||
|
@ -46,7 +49,7 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu
|
|||
private VelocityTracker velocityTracker;
|
||||
private int maximumFlingVelocity;
|
||||
private boolean isLockedToBottomEnd;
|
||||
private Queue<Runnable> runAfterFling;
|
||||
private Interpolator interpolator;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public static PictureInPictureGestureHelper applyTo(@NonNull View child) {
|
||||
|
@ -101,6 +104,7 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu
|
|||
this.pipHeight = child.getResources().getDimensionPixelSize(R.dimen.picture_in_picture_gesture_helper_pip_height);
|
||||
this.maximumFlingVelocity = ViewConfiguration.get(child.getContext()).getScaledMaximumFlingVelocity();
|
||||
this.runAfterFling = new LinkedList<>();
|
||||
this.interpolator = ADJUST_INTERPOLATOR;
|
||||
}
|
||||
|
||||
public void clearVerticalBoundaries() {
|
||||
|
@ -130,8 +134,12 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu
|
|||
pipHeight = child.getMeasuredHeight();
|
||||
|
||||
if (isAnimating) {
|
||||
interpolator = ADJUST_INTERPOLATOR;
|
||||
|
||||
fling();
|
||||
} else if (!isDragging) {
|
||||
interpolator = ADJUST_INTERPOLATOR;
|
||||
|
||||
onFling(null, null, 0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -160,6 +168,7 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu
|
|||
isDragging = true;
|
||||
pipWidth = child.getMeasuredWidth();
|
||||
pipHeight = child.getMeasuredHeight();
|
||||
interpolator = FLING_INTERPOLATOR;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -216,7 +225,7 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu
|
|||
.translationX(getTranslationXForPoint(nearestCornerPosition))
|
||||
.translationY(getTranslationYForPoint(nearestCornerPosition))
|
||||
.setDuration(250)
|
||||
.setInterpolator(new ViscousFluidInterpolator())
|
||||
.setInterpolator(interpolator)
|
||||
.setListener(new AnimationCompleteListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
|
|
|
@ -14,13 +14,20 @@ import org.thoughtcrime.securesms.events.CallParticipant;
|
|||
|
||||
class WebRtcCallParticipantsRecyclerAdapter extends ListAdapter<CallParticipant, WebRtcCallParticipantsRecyclerAdapter.ViewHolder> {
|
||||
|
||||
private static final int PARTICIPANT = 0;
|
||||
private static final int EMPTY = 1;
|
||||
|
||||
protected WebRtcCallParticipantsRecyclerAdapter() {
|
||||
super(new DiffCallback());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.webrtc_call_participant_recycler_item, parent, false));
|
||||
if (viewType == PARTICIPANT) {
|
||||
return new ParticipantViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.webrtc_call_participant_recycler_item, parent, false));
|
||||
} else {
|
||||
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.webrtc_call_participant_recycler_empty_item, parent, false));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,15 +35,29 @@ class WebRtcCallParticipantsRecyclerAdapter extends ListAdapter<CallParticipant,
|
|||
holder.bind(getItem(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return getItem(position) == CallParticipant.EMPTY ? EMPTY : PARTICIPANT;
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
void bind(@NonNull CallParticipant callParticipant) {}
|
||||
}
|
||||
|
||||
private static class ParticipantViewHolder extends ViewHolder {
|
||||
|
||||
private final CallParticipantView callParticipantView;
|
||||
|
||||
ViewHolder(@NonNull View itemView) {
|
||||
ParticipantViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
callParticipantView = itemView.findViewById(R.id.call_participant);
|
||||
}
|
||||
|
||||
@Override
|
||||
void bind(@NonNull CallParticipant callParticipant) {
|
||||
callParticipantView.setCallParticipant(callParticipant);
|
||||
}
|
||||
|
|
|
@ -20,8 +20,10 @@ import androidx.constraintlayout.widget.Guideline;
|
|||
import androidx.core.util.Consumer;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.AutoTransition;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import androidx.transition.Transition;
|
||||
import androidx.transition.TransitionManager;
|
||||
import androidx.transition.TransitionSet;
|
||||
import androidx.viewpager2.widget.MarginPageTransformer;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
|
@ -51,6 +53,7 @@ public class WebRtcCallView extends FrameLayout {
|
|||
|
||||
public static final int FADE_OUT_DELAY = 5000;
|
||||
public static final int PIP_RESIZE_DURATION = 300;
|
||||
public static final int CONTROLS_HEIGHT = 98;
|
||||
|
||||
private WebRtcAudioOutputToggleButton audioToggle;
|
||||
private AccessibleToggleButton videoToggle;
|
||||
|
@ -62,6 +65,7 @@ public class WebRtcCallView extends FrameLayout {
|
|||
private TextView recipientName;
|
||||
private TextView status;
|
||||
private ConstraintLayout parent;
|
||||
private ConstraintLayout participantsParent;
|
||||
private ControlsListener controlsListener;
|
||||
private RecipientId recipientId;
|
||||
private ImageView answer;
|
||||
|
@ -75,6 +79,8 @@ public class WebRtcCallView extends FrameLayout {
|
|||
private ViewPager2 callParticipantsPager;
|
||||
private RecyclerView callParticipantsRecycler;
|
||||
private Toolbar toolbar;
|
||||
private int pagerBottomMarginDp;
|
||||
private boolean controlsVisible = true;
|
||||
|
||||
private WebRtcCallParticipantsPagerAdapter pagerAdapter;
|
||||
private WebRtcCallParticipantsRecyclerAdapter recyclerAdapter;
|
||||
|
@ -114,6 +120,7 @@ public class WebRtcCallView extends FrameLayout {
|
|||
recipientName = findViewById(R.id.call_screen_recipient_name);
|
||||
status = findViewById(R.id.call_screen_status);
|
||||
parent = findViewById(R.id.call_screen);
|
||||
participantsParent = findViewById(R.id.call_screen_participants_parent);
|
||||
answer = findViewById(R.id.call_screen_answer_call);
|
||||
cameraDirectionToggle = findViewById(R.id.call_screen_camera_direction_toggle);
|
||||
hangup = findViewById(R.id.call_screen_end_call);
|
||||
|
@ -229,6 +236,12 @@ public class WebRtcCallView extends FrameLayout {
|
|||
pagerAdapter.submitList(pages);
|
||||
recyclerAdapter.submitList(state.getListParticipants());
|
||||
updateLocalCallParticipant(state.getLocalRenderState(), state.getLocalParticipant());
|
||||
|
||||
if (state.isLargeVideoGroup()) {
|
||||
layoutParticipantsForLargeCount();
|
||||
} else {
|
||||
layoutParticipantsForSmallCount();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLocalCallParticipant(@NonNull WebRtcLocalRenderState state, @NonNull CallParticipant localCallParticipant) {
|
||||
|
@ -371,6 +384,12 @@ public class WebRtcCallView extends FrameLayout {
|
|||
updateButtonStateForLargeButtons();
|
||||
}
|
||||
|
||||
if (webRtcControls.displayRemoteVideoRecycler()) {
|
||||
callParticipantsRecycler.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
callParticipantsRecycler.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (webRtcControls.isFadeOutEnabled()) {
|
||||
if (!controls.isFadeOutEnabled()) {
|
||||
scheduleFadeOut();
|
||||
|
@ -443,9 +462,44 @@ public class WebRtcCallView extends FrameLayout {
|
|||
scheduleFadeOut();
|
||||
}
|
||||
|
||||
private void fadeControls(int visibility) {
|
||||
private void layoutParticipantsForSmallCount() {
|
||||
pagerBottomMarginDp = 0;
|
||||
|
||||
layoutParticipants();
|
||||
}
|
||||
|
||||
private void layoutParticipantsForLargeCount() {
|
||||
pagerBottomMarginDp = 104;
|
||||
|
||||
layoutParticipants();
|
||||
}
|
||||
|
||||
private int withControlsHeight(int margin) {
|
||||
if (margin == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return controlsVisible ? margin + CONTROLS_HEIGHT : margin;
|
||||
}
|
||||
|
||||
private void layoutParticipants() {
|
||||
Transition transition = new AutoTransition().setDuration(TRANSITION_DURATION_MILLIS);
|
||||
|
||||
TransitionManager.beginDelayedTransition(participantsParent, transition);
|
||||
|
||||
ConstraintSet constraintSet = new ConstraintSet();
|
||||
constraintSet.clone(participantsParent);
|
||||
|
||||
constraintSet.setMargin(R.id.call_screen_participants_pager, ConstraintSet.BOTTOM, ViewUtil.dpToPx(withControlsHeight(pagerBottomMarginDp)));
|
||||
constraintSet.applyTo(participantsParent);
|
||||
}
|
||||
|
||||
private void fadeControls(int visibility) {
|
||||
controlsVisible = visibility == VISIBLE;
|
||||
|
||||
Transition transition = new AutoTransition().setOrdering(TransitionSet.ORDERING_TOGETHER)
|
||||
.setDuration(TRANSITION_DURATION_MILLIS);
|
||||
|
||||
TransitionManager.beginDelayedTransition(parent, transition);
|
||||
|
||||
ConstraintSet constraintSet = new ConstraintSet();
|
||||
|
@ -456,6 +510,8 @@ public class WebRtcCallView extends FrameLayout {
|
|||
}
|
||||
|
||||
constraintSet.applyTo(parent);
|
||||
|
||||
layoutParticipants();
|
||||
}
|
||||
|
||||
private void fadeInNewUiState(@NonNull Set<View> previouslyVisibleViewSet, boolean useSmallMargins) {
|
||||
|
|
|
@ -147,6 +147,18 @@ public class WebRtcCallViewModel extends ViewModel {
|
|||
callState = WebRtcControls.CallState.INCOMING;
|
||||
answerWithVideoAvailable = isRemoteVideoOffer;
|
||||
break;
|
||||
case CALL_OUTGOING:
|
||||
case CALL_RINGING:
|
||||
callState = WebRtcControls.CallState.OUTGOING;
|
||||
break;
|
||||
case CALL_ACCEPTED_ELSEWHERE:
|
||||
case CALL_DECLINED_ELSEWHERE:
|
||||
case CALL_ONGOING_ELSEWHERE:
|
||||
case CALL_NEEDS_PERMISSION:
|
||||
case CALL_BUSY:
|
||||
case CALL_DISCONNECTED:
|
||||
callState = WebRtcControls.CallState.ENDING;
|
||||
break;
|
||||
default:
|
||||
callState = WebRtcControls.CallState.ONGOING;
|
||||
}
|
||||
|
|
|
@ -41,23 +41,27 @@ public final class WebRtcControls {
|
|||
}
|
||||
|
||||
boolean displayEndCall() {
|
||||
return isOngoing();
|
||||
return isAtLeastOutgoing();
|
||||
}
|
||||
|
||||
boolean displayMuteAudio() {
|
||||
return isOngoing();
|
||||
return isAtLeastOutgoing();
|
||||
}
|
||||
|
||||
boolean displayVideoToggle() {
|
||||
return isOngoing();
|
||||
return isAtLeastOutgoing();
|
||||
}
|
||||
|
||||
boolean displayAudioToggle() {
|
||||
return isOngoing() && (!isLocalVideoEnabled || isBluetoothAvailable);
|
||||
return isAtLeastOutgoing() && (!isLocalVideoEnabled || isBluetoothAvailable);
|
||||
}
|
||||
|
||||
boolean displayCameraToggle() {
|
||||
return isOngoing() && isLocalVideoEnabled && isMoreThanOneCameraAvailable;
|
||||
return isAtLeastOutgoing() && isLocalVideoEnabled && isMoreThanOneCameraAvailable;
|
||||
}
|
||||
|
||||
boolean displayRemoteVideoRecycler() {
|
||||
return isOngoing();
|
||||
}
|
||||
|
||||
boolean displayAnswerWithAudio() {
|
||||
|
@ -77,15 +81,15 @@ public final class WebRtcControls {
|
|||
}
|
||||
|
||||
boolean isFadeOutEnabled() {
|
||||
return isOngoing() && isRemoteVideoEnabled;
|
||||
return isAtLeastOutgoing() && isRemoteVideoEnabled;
|
||||
}
|
||||
|
||||
boolean displaySmallOngoingCallButtons() {
|
||||
return isOngoing() && displayAudioToggle() && displayCameraToggle();
|
||||
return isAtLeastOutgoing() && displayAudioToggle() && displayCameraToggle();
|
||||
}
|
||||
|
||||
boolean displayLargeOngoingCallButtons() {
|
||||
return isOngoing() && !(displayAudioToggle() && displayCameraToggle());
|
||||
return isAtLeastOutgoing() && !(displayAudioToggle() && displayCameraToggle());
|
||||
}
|
||||
|
||||
boolean displayTopViews() {
|
||||
|
@ -104,9 +108,19 @@ public final class WebRtcControls {
|
|||
return callState == CallState.INCOMING;
|
||||
}
|
||||
|
||||
private boolean isAtLeastOutgoing() {
|
||||
return callState.isAtLeast(CallState.OUTGOING);
|
||||
}
|
||||
|
||||
public enum CallState {
|
||||
NONE,
|
||||
INCOMING,
|
||||
ONGOING
|
||||
OUTGOING,
|
||||
ONGOING,
|
||||
ENDING;
|
||||
|
||||
boolean isAtLeast(@NonNull CallState other) {
|
||||
return compareTo(other) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.Objects;
|
|||
|
||||
public class CallParticipant {
|
||||
|
||||
public static final CallParticipant EMPTY = createRemote(Recipient.UNKNOWN, null, new BroadcastVideoSink(null), false);
|
||||
|
||||
private final @NonNull CameraState cameraState;
|
||||
private final @NonNull Recipient recipient;
|
||||
private final @Nullable IdentityKey identityKey;
|
||||
|
|
|
@ -78,6 +78,7 @@ import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserExce
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Space xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
tools:background="@color/red"
|
||||
tools:visibility="visible" />
|
|
@ -11,6 +11,7 @@
|
|||
android:background="@color/transparent_black_40" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/call_screen_participants_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
@ -24,19 +25,6 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/call_screen_participants_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:reverseLayout="true"
|
||||
tools:listitem="@layout/webrtc_call_participant_recycler_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<FrameLayout
|
||||
|
@ -93,6 +81,20 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/call_screen_participants_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="72dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toTopOf="@id/call_screen_video_toggle"
|
||||
app:reverseLayout="true"
|
||||
tools:listitem="@layout/webrtc_call_participant_recycler_item" />
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout
|
||||
android:id="@+id/call_screen_pip_area"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
|
||||
<dimen name="debug_log_text_size">12sp</dimen>
|
||||
|
||||
<dimen name="picture_in_picture_gesture_helper_frame_padding">12dp</dimen>
|
||||
<dimen name="picture_in_picture_gesture_helper_frame_padding">16dp</dimen>
|
||||
<dimen name="picture_in_picture_gesture_helper_pip_width">90dp</dimen>
|
||||
<dimen name="picture_in_picture_gesture_helper_pip_height">160dp</dimen>
|
||||
|
||||
|
|
Loading…
Reference in New Issue