mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Move switch camera button to self pip.
This commit is contained in:
@@ -148,6 +148,7 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
|
||||
private FullscreenHelper fullscreenHelper;
|
||||
private WebRtcCallView callScreen;
|
||||
private TooltipPopup videoTooltip;
|
||||
private TooltipPopup switchCameraTooltip;
|
||||
private WebRtcCallViewModel viewModel;
|
||||
private boolean enableVideoIfAvailable;
|
||||
private boolean hasWarnedAboutBluetooth;
|
||||
@@ -549,6 +550,20 @@ public class WebRtcCallActivity extends BaseActivity implements SafetyNumberChan
|
||||
}
|
||||
} else if (event instanceof WebRtcCallViewModel.Event.ShowWifiToCellularPopup) {
|
||||
wifiToCellularPopupWindow.show();
|
||||
} else if (event instanceof WebRtcCallViewModel.Event.ShowSwitchCameraTooltip) {
|
||||
if (switchCameraTooltip == null) {
|
||||
switchCameraTooltip = TooltipPopup.forTarget(callScreen.getSwitchCameraTooltipTarget())
|
||||
.setBackgroundTint(ContextCompat.getColor(this, R.color.core_ultramarine))
|
||||
.setTextColor(ContextCompat.getColor(this, R.color.core_white))
|
||||
.setText(R.string.WebRtcCallActivity__flip_camera_tooltip)
|
||||
.setOnDismissListener(() -> viewModel.onDismissedSwitchCameraTooltip())
|
||||
.show(TooltipPopup.POSITION_ABOVE);
|
||||
}
|
||||
} else if (event instanceof WebRtcCallViewModel.Event.DismissSwitchCameraTooltip) {
|
||||
if (switchCameraTooltip != null) {
|
||||
switchCameraTooltip.dismiss();
|
||||
switchCameraTooltip = null;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown event: " + event);
|
||||
}
|
||||
|
||||
@@ -171,21 +171,23 @@ public class TooltipPopup extends PopupWindow {
|
||||
ShapeAppearanceModel.Builder shapeAppearanceModel = ShapeAppearanceModel.builder()
|
||||
.setAllCornerSizes(DimensionUnit.DP.toPixels(18));
|
||||
|
||||
// If the arrow is within the last 20dp of the right hand side, use RIGHT and set corner to 9dp
|
||||
onLayout(() -> {
|
||||
if (arrow.getX() > getContentView().getWidth() / 2f) {
|
||||
arrow.setImageResource(R.drawable.ic_tooltip_arrow_up_right);
|
||||
}
|
||||
if (position == POSITION_BELOW) {
|
||||
// If the arrow is within the last 20dp of the right hand side, use RIGHT and set corner to 9dp
|
||||
onLayout(() -> {
|
||||
if (arrow.getX() > getContentView().getWidth() / 2f) {
|
||||
arrow.setImageResource(R.drawable.ic_tooltip_arrow_up_right);
|
||||
}
|
||||
|
||||
float arrowEnd = arrow.getX() + arrow.getRight();
|
||||
if (arrowEnd > getContentView().getRight() - DimensionUnit.DP.toPixels(20)) {
|
||||
shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopRightCornerSize(DimensionUnit.DP.toPixels(9f)).build());
|
||||
bubble.setBackground(shapeableBubbleBackground);
|
||||
} else if (arrowEnd < DimensionUnit.DP.toPixels(20)) {
|
||||
shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopLeftCornerSize(DimensionUnit.DP.toPixels(9f)).build());
|
||||
bubble.setBackground(shapeableBubbleBackground);
|
||||
}
|
||||
});
|
||||
float arrowEnd = arrow.getX() + arrow.getRight();
|
||||
if (arrowEnd > getContentView().getRight() - DimensionUnit.DP.toPixels(20)) {
|
||||
shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopRightCornerSize(DimensionUnit.DP.toPixels(9f)).build());
|
||||
bubble.setBackground(shapeableBubbleBackground);
|
||||
} else if (arrowEnd < DimensionUnit.DP.toPixels(20)) {
|
||||
shapeableBubbleBackground.setShapeAppearanceModel(shapeAppearanceModel.setTopLeftCornerSize(DimensionUnit.DP.toPixels(9f)).build());
|
||||
bubble.setBackground(shapeableBubbleBackground);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
showAsDropDown(anchor, xoffset, yoffset);
|
||||
|
||||
@@ -74,6 +74,8 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
private EmojiTextView infoMessage;
|
||||
private Button infoMoreInfo;
|
||||
private AppCompatImageView infoIcon;
|
||||
private View switchCameraIconFrame;
|
||||
private View switchCameraIcon;
|
||||
|
||||
public CallParticipantView(@NonNull Context context) {
|
||||
super(context);
|
||||
@@ -92,18 +94,20 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
backgroundAvatar = findViewById(R.id.call_participant_background_avatar);
|
||||
avatar = findViewById(R.id.call_participant_item_avatar);
|
||||
pipAvatar = findViewById(R.id.call_participant_item_pip_avatar);
|
||||
rendererFrame = findViewById(R.id.call_participant_renderer_frame);
|
||||
renderer = findViewById(R.id.call_participant_renderer);
|
||||
audioIndicator = findViewById(R.id.call_participant_audio_indicator);
|
||||
infoOverlay = findViewById(R.id.call_participant_info_overlay);
|
||||
infoIcon = findViewById(R.id.call_participant_info_icon);
|
||||
infoMessage = findViewById(R.id.call_participant_info_message);
|
||||
infoMoreInfo = findViewById(R.id.call_participant_info_more_info);
|
||||
badge = findViewById(R.id.call_participant_item_badge);
|
||||
pipBadge = findViewById(R.id.call_participant_item_pip_badge);
|
||||
backgroundAvatar = findViewById(R.id.call_participant_background_avatar);
|
||||
avatar = findViewById(R.id.call_participant_item_avatar);
|
||||
pipAvatar = findViewById(R.id.call_participant_item_pip_avatar);
|
||||
rendererFrame = findViewById(R.id.call_participant_renderer_frame);
|
||||
renderer = findViewById(R.id.call_participant_renderer);
|
||||
audioIndicator = findViewById(R.id.call_participant_audio_indicator);
|
||||
infoOverlay = findViewById(R.id.call_participant_info_overlay);
|
||||
infoIcon = findViewById(R.id.call_participant_info_icon);
|
||||
infoMessage = findViewById(R.id.call_participant_info_message);
|
||||
infoMoreInfo = findViewById(R.id.call_participant_info_more_info);
|
||||
badge = findViewById(R.id.call_participant_item_badge);
|
||||
pipBadge = findViewById(R.id.call_participant_item_pip_badge);
|
||||
switchCameraIconFrame = findViewById(R.id.call_participant_switch_camera);
|
||||
switchCameraIcon = findViewById(R.id.call_participant_switch_camera_icon);
|
||||
|
||||
avatar.setFallbackPhotoProvider(FALLBACK_PHOTO_PROVIDER);
|
||||
useLargeAvatar();
|
||||
@@ -249,6 +253,27 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
ConstraintSet.BOTTOM,
|
||||
ViewUtil.dpToPx(6)
|
||||
);
|
||||
|
||||
constraints.setVisibility(R.id.call_participant_switch_camera, View.VISIBLE);
|
||||
constraints.setMargin(
|
||||
R.id.call_participant_switch_camera,
|
||||
ConstraintSet.END,
|
||||
ViewUtil.dpToPx(6)
|
||||
);
|
||||
constraints.setMargin(
|
||||
R.id.call_participant_switch_camera,
|
||||
ConstraintSet.BOTTOM,
|
||||
ViewUtil.dpToPx(6)
|
||||
);
|
||||
constraints.constrainWidth(R.id.call_participant_switch_camera, ViewUtil.dpToPx(28));
|
||||
constraints.constrainHeight(R.id.call_participant_switch_camera, ViewUtil.dpToPx(28));
|
||||
|
||||
ViewGroup.LayoutParams params = switchCameraIcon.getLayoutParams();
|
||||
params.width = params.height = ViewUtil.dpToPx(16);
|
||||
switchCameraIcon.setLayoutParams(params);
|
||||
|
||||
switchCameraIconFrame.setClickable(false);
|
||||
switchCameraIconFrame.setEnabled(false);
|
||||
}
|
||||
case EXPANDED_SELF_PIP -> {
|
||||
constraints.connect(
|
||||
@@ -267,6 +292,27 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
ConstraintSet.BOTTOM,
|
||||
ViewUtil.dpToPx(8)
|
||||
);
|
||||
|
||||
constraints.setVisibility(R.id.call_participant_switch_camera, View.VISIBLE);
|
||||
constraints.setMargin(
|
||||
R.id.call_participant_switch_camera,
|
||||
ConstraintSet.END,
|
||||
ViewUtil.dpToPx(8)
|
||||
);
|
||||
constraints.setMargin(
|
||||
R.id.call_participant_switch_camera,
|
||||
ConstraintSet.BOTTOM,
|
||||
ViewUtil.dpToPx(8)
|
||||
);
|
||||
constraints.constrainWidth(R.id.call_participant_switch_camera, ViewUtil.dpToPx(48));
|
||||
constraints.constrainHeight(R.id.call_participant_switch_camera, ViewUtil.dpToPx(48));
|
||||
|
||||
ViewGroup.LayoutParams params = switchCameraIcon.getLayoutParams();
|
||||
params.width = params.height = ViewUtil.dpToPx(24);
|
||||
switchCameraIcon.setLayoutParams(params);
|
||||
|
||||
switchCameraIconFrame.setClickable(true);
|
||||
switchCameraIconFrame.setEnabled(true);
|
||||
}
|
||||
case MINI_SELF_PIP -> {
|
||||
constraints.connect(
|
||||
@@ -288,6 +334,7 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
ConstraintSet.BOTTOM,
|
||||
ViewUtil.dpToPx(6)
|
||||
);
|
||||
constraints.setVisibility(R.id.call_participant_switch_camera, View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,6 +278,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
|
||||
});
|
||||
|
||||
cameraDirectionToggle.setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onCameraDirectionChanged));
|
||||
smallLocalRender.findViewById(R.id.call_participant_switch_camera).setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onCameraDirectionChanged));
|
||||
|
||||
overflow.setOnClickListener(v -> {
|
||||
runIfNonNull(controlsListener, ControlsListener::onOverflowClicked);
|
||||
@@ -794,6 +795,10 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
|
||||
return videoToggle;
|
||||
}
|
||||
|
||||
public @NonNull View getSwitchCameraTooltipTarget() {
|
||||
return smallLocalRenderFrame;
|
||||
}
|
||||
|
||||
public void showSpeakerViewHint() {
|
||||
groupCallSpeakerHint.get().setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -81,17 +81,18 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
private final Runnable elapsedTimeRunnable = this::handleTick;
|
||||
private final Runnable stopOutgoingRingingMode = this::stopOutgoingRingingMode;
|
||||
|
||||
private boolean canDisplayTooltipIfNeeded = true;
|
||||
private boolean canDisplayPopupIfNeeded = true;
|
||||
private boolean hasEnabledLocalVideo = false;
|
||||
private boolean wasInOutgoingRingingMode = false;
|
||||
private long callConnectedTime = -1;
|
||||
private boolean answerWithVideoAvailable = false;
|
||||
private boolean canEnterPipMode = false;
|
||||
private List<CallParticipant> previousParticipantsList = Collections.emptyList();
|
||||
private boolean callStarting = false;
|
||||
private boolean switchOnFirstScreenShare = true;
|
||||
private boolean showScreenShareTip = true;
|
||||
private boolean canDisplayTooltipIfNeeded = true;
|
||||
private boolean canDisplaySwitchCameraTooltipIfNeeded = true;
|
||||
private boolean canDisplayPopupIfNeeded = true;
|
||||
private boolean hasEnabledLocalVideo = false;
|
||||
private boolean wasInOutgoingRingingMode = false;
|
||||
private long callConnectedTime = -1;
|
||||
private boolean answerWithVideoAvailable = false;
|
||||
private boolean canEnterPipMode = false;
|
||||
private List<CallParticipant> previousParticipantsList = Collections.emptyList();
|
||||
private boolean callStarting = false;
|
||||
private boolean switchOnFirstScreenShare = true;
|
||||
private boolean showScreenShareTip = true;
|
||||
|
||||
private final WebRtcCallRepository repository = new WebRtcCallRepository(ApplicationDependencies.getApplication());
|
||||
|
||||
@@ -261,6 +262,11 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
canDisplayTooltipIfNeeded = false;
|
||||
}
|
||||
|
||||
public void onDismissedSwitchCameraTooltip() {
|
||||
canDisplaySwitchCameraTooltipIfNeeded = false;
|
||||
SignalStore.tooltips().markCallingSwitchCameraTooltipSeen();
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void updateFromWebRtcViewModel(@NonNull WebRtcViewModel webRtcViewModel, boolean enableVideo) {
|
||||
canEnterPipMode = !webRtcViewModel.getState().isPreJoinOrNetworkUnavailable();
|
||||
@@ -342,6 +348,16 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
} else if (!webRtcViewModel.isCellularConnection()) {
|
||||
canDisplayPopupIfNeeded = true;
|
||||
}
|
||||
|
||||
if (SignalStore.tooltips().showCallingSwitchCameraTooltip() &&
|
||||
canDisplaySwitchCameraTooltipIfNeeded &&
|
||||
hasEnabledLocalVideo &&
|
||||
webRtcViewModel.getState() == WebRtcViewModel.State.CALL_CONNECTED &&
|
||||
!newState.getAllRemoteParticipants().isEmpty()
|
||||
) {
|
||||
canDisplaySwitchCameraTooltipIfNeeded = false;
|
||||
events.setValue(new Event.ShowSwitchCameraTooltip());
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@@ -537,6 +553,12 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
public static class ShowWifiToCellularPopup extends Event {
|
||||
}
|
||||
|
||||
public static class ShowSwitchCameraTooltip extends Event {
|
||||
}
|
||||
|
||||
public static class DismissSwitchCameraTooltip extends Event {
|
||||
}
|
||||
|
||||
public static class StartCall extends Event {
|
||||
private final boolean isVideoCall;
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ public final class WebRtcControls {
|
||||
}
|
||||
|
||||
public boolean displayOverflow() {
|
||||
return FeatureFlags.groupCallReactions() && isAtLeastOutgoing();
|
||||
return FeatureFlags.groupCallReactions() && isAtLeastOutgoing() && hasAtLeastOneRemote && isGroupCall();
|
||||
}
|
||||
|
||||
public boolean displayMuteAudio() {
|
||||
@@ -173,7 +173,7 @@ public final class WebRtcControls {
|
||||
}
|
||||
|
||||
public boolean displayCameraToggle() {
|
||||
return (isPreJoin() || isAtLeastOutgoing()) && isLocalVideoEnabled && isMoreThanOneCameraAvailable;
|
||||
return (isPreJoin() || (isAtLeastOutgoing() && !hasAtLeastOneRemote)) && isLocalVideoEnabled && isMoreThanOneCameraAvailable;
|
||||
}
|
||||
|
||||
public boolean displayRemoteVideoRecycler() {
|
||||
|
||||
@@ -15,7 +15,7 @@ public class TooltipValues extends SignalStoreValues {
|
||||
private static final String MULTI_FORWARD_DIALOG = "tooltip.multi.forward.dialog";
|
||||
private static final String BUBBLE_OPT_OUT = "tooltip.bubble.opt.out";
|
||||
private static final String PROFILE_SETTINGS_QR_CODE = "tooltip.profile_settings_qr_code";
|
||||
|
||||
private static final String CALLING_SWITCH_CAMERA = "tooltip.calling.switch_camera";
|
||||
|
||||
TooltipValues(@NonNull KeyValueStore store) {
|
||||
super(store);
|
||||
@@ -82,4 +82,12 @@ public class TooltipValues extends SignalStoreValues {
|
||||
public void markProfileSettingsQrCodeTooltipSeen() {
|
||||
putBoolean(PROFILE_SETTINGS_QR_CODE, false);
|
||||
}
|
||||
|
||||
public boolean showCallingSwitchCameraTooltip() {
|
||||
return getBoolean(CALLING_SWITCH_CAMERA, true);
|
||||
}
|
||||
|
||||
public void markCallingSwitchCameraTooltipSeen() {
|
||||
putBoolean(CALLING_SWITCH_CAMERA, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.components.webrtc.CallParticipantView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:layout_height="match_parent"
|
||||
tools:layout_width="match_parent">
|
||||
tools:layout_width="match_parent"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/call_participant_background_avatar"
|
||||
@@ -93,6 +93,29 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/call_participant_switch_camera"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="@color/signal_dark_colorSecondaryContainer"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/call_participant_switch_camera_icon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_gravity="center"
|
||||
app:srcCompat="@drawable/symbol_switch_24"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/call_participant_info_overlay"
|
||||
android:layout_width="0dp"
|
||||
|
||||
@@ -2387,6 +2387,8 @@
|
||||
<string name="WebRtcCallActivity__answered_on_a_linked_device">Answered on a linked device.</string>
|
||||
<string name="WebRtcCallActivity__declined_on_a_linked_device">Declined on a linked device.</string>
|
||||
<string name="WebRtcCallActivity__busy_on_a_linked_device">Busy on a linked device.</string>
|
||||
<!-- Tooltip message shown first time user is in a video call after switch camera button moved -->
|
||||
<string name="WebRtcCallActivity__flip_camera_tooltip">Flip Camera has been moved here, tap your video to try it out</string>
|
||||
|
||||
<string name="GroupCallSafetyNumberChangeNotification__someone_has_joined_this_call_with_a_safety_number_that_has_changed">Someone has joined this call with a safety number that has changed.</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user