diff --git a/res/drawable-hdpi/ic_videocam_white_24dp.webp b/res/drawable-hdpi/ic_videocam_white_24dp.webp
deleted file mode 100644
index daacaeb526..0000000000
Binary files a/res/drawable-hdpi/ic_videocam_white_24dp.webp and /dev/null differ
diff --git a/res/drawable-hdpi/ic_videocam_white_48dp.webp b/res/drawable-hdpi/ic_videocam_white_48dp.webp
deleted file mode 100644
index 08afed92e0..0000000000
Binary files a/res/drawable-hdpi/ic_videocam_white_48dp.webp and /dev/null differ
diff --git a/res/drawable-mdpi/ic_videocam_white_24dp.webp b/res/drawable-mdpi/ic_videocam_white_24dp.webp
deleted file mode 100644
index 9dc5fa6d04..0000000000
Binary files a/res/drawable-mdpi/ic_videocam_white_24dp.webp and /dev/null differ
diff --git a/res/drawable-mdpi/ic_videocam_white_48dp.webp b/res/drawable-mdpi/ic_videocam_white_48dp.webp
deleted file mode 100644
index c5e4f5bab1..0000000000
Binary files a/res/drawable-mdpi/ic_videocam_white_48dp.webp and /dev/null differ
diff --git a/res/drawable-xhdpi/ic_videocam_white_24dp.webp b/res/drawable-xhdpi/ic_videocam_white_24dp.webp
deleted file mode 100644
index c5e4f5bab1..0000000000
Binary files a/res/drawable-xhdpi/ic_videocam_white_24dp.webp and /dev/null differ
diff --git a/res/drawable-xhdpi/ic_videocam_white_48dp.webp b/res/drawable-xhdpi/ic_videocam_white_48dp.webp
deleted file mode 100644
index 0ac5fc5798..0000000000
Binary files a/res/drawable-xhdpi/ic_videocam_white_48dp.webp and /dev/null differ
diff --git a/res/drawable-xxhdpi/ic_call_white_24dp.webp b/res/drawable-xxhdpi/ic_call_white_24dp.webp
deleted file mode 100644
index 41dc409e31..0000000000
Binary files a/res/drawable-xxhdpi/ic_call_white_24dp.webp and /dev/null differ
diff --git a/res/drawable-xxhdpi/ic_videocam_white_24dp.webp b/res/drawable-xxhdpi/ic_videocam_white_24dp.webp
deleted file mode 100644
index 08afed92e0..0000000000
Binary files a/res/drawable-xxhdpi/ic_videocam_white_24dp.webp and /dev/null differ
diff --git a/res/drawable-xxhdpi/ic_videocam_white_48dp.webp b/res/drawable-xxhdpi/ic_videocam_white_48dp.webp
deleted file mode 100644
index 8ccb2d0de9..0000000000
Binary files a/res/drawable-xxhdpi/ic_videocam_white_48dp.webp and /dev/null differ
diff --git a/res/drawable-xxxhdpi/ic_videocam_white_24dp.webp b/res/drawable-xxxhdpi/ic_videocam_white_24dp.webp
deleted file mode 100644
index 0ac5fc5798..0000000000
Binary files a/res/drawable-xxxhdpi/ic_videocam_white_24dp.webp and /dev/null differ
diff --git a/res/drawable-xxxhdpi/ic_videocam_white_48dp.webp b/res/drawable-xxxhdpi/ic_videocam_white_48dp.webp
deleted file mode 100644
index 05c37229ea..0000000000
Binary files a/res/drawable-xxxhdpi/ic_videocam_white_48dp.webp and /dev/null differ
diff --git a/res/drawable/ic_phone_right_solid_24.xml b/res/drawable/ic_phone_right_solid_24.xml
new file mode 100644
index 0000000000..a523efa007
--- /dev/null
+++ b/res/drawable/ic_phone_right_solid_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/res/drawable/ic_phone_right_unlock_solid_24.xml b/res/drawable/ic_phone_right_unlock_solid_24.xml
new file mode 100644
index 0000000000..e3321a7147
--- /dev/null
+++ b/res/drawable/ic_phone_right_unlock_solid_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/res/drawable/ic_video_solid_24.xml b/res/drawable/ic_video_solid_24.xml
new file mode 100644
index 0000000000..7a2fa3ec99
--- /dev/null
+++ b/res/drawable/ic_video_solid_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/res/drawable/ic_video_solid_24_tinted.xml b/res/drawable/ic_video_solid_24_tinted.xml
new file mode 100644
index 0000000000..8d2b24b0f1
--- /dev/null
+++ b/res/drawable/ic_video_solid_24_tinted.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/res/drawable/webrtc_video_mute_button.xml b/res/drawable/webrtc_video_mute_button.xml
index 5cc40e123a..51a8733ed2 100644
--- a/res/drawable/webrtc_video_mute_button.xml
+++ b/res/drawable/webrtc_video_mute_button.xml
@@ -5,5 +5,5 @@
android:left="5dp"
android:right="5dp"
android:bottom="5dp"
- android:drawable="@drawable/ic_videocam_white_24dp"/>
+ android:drawable="@drawable/ic_video_solid_24"/>
\ No newline at end of file
diff --git a/res/layout/conversation_title_view.xml b/res/layout/conversation_title_view.xml
index ffa7673f8e..a5f9098e66 100644
--- a/res/layout/conversation_title_view.xml
+++ b/res/layout/conversation_title_view.xml
@@ -53,14 +53,15 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
+
+
@@ -74,6 +75,7 @@
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
+ android:textColor="?conversation_subtitle_color"
android:text="@string/ConversationTitleView_verified"
android:textDirection="ltr" />
@@ -85,6 +87,7 @@
android:layout_gravity="center_vertical|start"
android:ellipsize="end"
android:gravity="center_vertical"
+ android:textColor="?conversation_subtitle_color"
android:maxLines="1"
android:textDirection="ltr"
tools:text="(123) 123-1234" />
diff --git a/res/layout/expiration_timer_badge.xml b/res/layout/expiration_timer_badge.xml
new file mode 100644
index 0000000000..aa023058f9
--- /dev/null
+++ b/res/layout/expiration_timer_badge.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/expiration_timer_menu.xml b/res/layout/expiration_timer_menu.xml
deleted file mode 100644
index a4c52797e9..0000000000
--- a/res/layout/expiration_timer_menu.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/item_selectable_contact_field.xml b/res/layout/item_selectable_contact_field.xml
index 8074933c2a..7161a100c2 100644
--- a/res/layout/item_selectable_contact_field.xml
+++ b/res/layout/item_selectable_contact_field.xml
@@ -15,7 +15,7 @@
android:layout_height="24dp"
android:layout_margin="16dp"
android:tint="@color/grey_600"
- tools:src="@drawable/ic_call_white_24dp" />
+ tools:src="@drawable/ic_phone_right_unlock_solid_24" />
-
+
-
+
-
+
+
+
\ No newline at end of file
diff --git a/res/layout/webrtc_call_screen.xml b/res/layout/webrtc_call_screen.xml
index bbefd15a34..3d0247e814 100644
--- a/res/layout/webrtc_call_screen.xml
+++ b/res/layout/webrtc_call_screen.xml
@@ -26,6 +26,12 @@
android:layout_height="match_parent"
tools:visibility="invisible" />
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/conversation_expiring_on.xml b/res/menu/conversation_expiring_on.xml
index 5dbf88b001..cb9cba3269 100644
--- a/res/menu/conversation_expiring_on.xml
+++ b/res/menu/conversation_expiring_on.xml
@@ -3,8 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
+ app:showAsAction="never"
+ android:title="@string/conversation_expiring_off__disappearing_messages"/>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 22fdba048a..306e474964 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -120,6 +120,10 @@
No web browser found.
A cellular call is already in progress.
+ Start video call?
+ Start voice call?
+ Cancel
+ Call
Your safety number with %1$s has changed. This could either mean that someone is trying to intercept your communication, or that %2$s simply reinstalled Signal.
@@ -1453,6 +1457,7 @@
Signal call
+ Signal video call
Message details
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 33b72463b5..4e0e00af87 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -302,7 +302,7 @@
- @drawable/ic_add_white_24dp
- @drawable/ic_group_solid_24
- @drawable/ic_search_24
- - @drawable/ic_call_white_24dp
+ - @drawable/ic_phone_right_unlock_solid_24
- @drawable/ic_launch_white_24dp
- @drawable/ic_unlocked_white_24dp
- @drawable/ic_lock_white_24dp
@@ -532,7 +532,7 @@
- @drawable/ic_add_white_24dp
- @drawable/ic_group_solid_24
- @drawable/ic_search_24
- - @drawable/ic_call_white_24dp
+ - @drawable/ic_phone_right_unlock_solid_24
- @drawable/ic_launch_white_24dp
- @drawable/ic_unlocked_white_24dp
- @drawable/ic_lock_white_24dp
diff --git a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java
index cd9907c143..5af3f19036 100644
--- a/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java
+++ b/src/org/thoughtcrime/securesms/RecipientPreferenceActivity.java
@@ -769,6 +769,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
CommunicationActions.startVoiceCall(getActivity(), recipient.get());
}
+ @Override
+ public void onSecureVideoClicked() {
+ CommunicationActions.startVideoCall(getActivity(), recipient.get());
+ }
+
@Override
public void onInSecureCallClicked() {
try {
diff --git a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
index 3d43b58029..81e37e6c66 100644
--- a/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
+++ b/src/org/thoughtcrime/securesms/WebRtcCallActivity.java
@@ -49,6 +49,7 @@ import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
+import org.webrtc.SurfaceViewRenderer;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.SignalProtocolAddress;
@@ -65,7 +66,10 @@ public class WebRtcCallActivity extends Activity {
public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION";
public static final String END_CALL_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".END_CALL_ACTION";
- private WebRtcCallScreen callScreen;
+ public static final String EXTRA_ENABLE_VIDEO_IF_AVAILABLE = WebRtcCallActivity.class.getCanonicalName() + ".ENABLE_VIDEO_IF_AVAILABLE";
+
+ private WebRtcCallScreen callScreen;
+ private boolean enableVideoIfAvailable;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -82,6 +86,9 @@ public class WebRtcCallActivity extends Activity {
initializeResources();
processIntent(getIntent());
+
+ enableVideoIfAvailable = getIntent().getBooleanExtra(EXTRA_ENABLE_VIDEO_IF_AVAILABLE, false);
+ getIntent().removeExtra(EXTRA_ENABLE_VIDEO_IF_AVAILABLE);
}
@@ -173,10 +180,10 @@ public class WebRtcCallActivity extends Activity {
.request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
.ifNecessary()
.withRationaleDialog(getString(R.string.WebRtcCallActivity_to_answer_the_call_from_s_give_signal_access_to_your_microphone, event.getRecipient().toShortString(this)),
- R.drawable.ic_mic_solid_24, R.drawable.ic_videocam_white_48dp)
+ R.drawable.ic_mic_solid_24, R.drawable.ic_video_solid_24_tinted)
.withPermanentDenialDialog(getString(R.string.WebRtcCallActivity_signal_requires_microphone_and_camera_permissions_in_order_to_make_or_receive_calls))
.onAllGranted(() -> {
- callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_answering));
+ callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_answering), event.getLocalRenderer());
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_ANSWER_CALL);
@@ -195,7 +202,7 @@ public class WebRtcCallActivity extends Activity {
intent.setAction(WebRtcCallService.ACTION_DENY_CALL);
startService(intent);
- callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ending_call));
+ callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ending_call), event.getLocalRenderer());
delayedFinish();
}
}
@@ -212,24 +219,24 @@ public class WebRtcCallActivity extends Activity {
}
private void handleOutgoingCall(@NonNull WebRtcViewModel event) {
- callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_dialing));
+ callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_dialing), event.getLocalRenderer());
}
- private void handleTerminate(@NonNull Recipient recipient /*, int terminationType */) {
+ private void handleTerminate(@NonNull Recipient recipient, @NonNull SurfaceViewRenderer localRenderer /*, int terminationType */) {
Log.i(TAG, "handleTerminate called");
- callScreen.setActiveCall(recipient, getString(R.string.RedPhone_ending_call));
+ callScreen.setActiveCall(recipient, getString(R.string.RedPhone_ending_call), localRenderer);
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
delayedFinish();
}
private void handleCallRinging(@NonNull WebRtcViewModel event) {
- callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ringing));
+ callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_ringing), event.getLocalRenderer());
}
private void handleCallBusy(@NonNull WebRtcViewModel event) {
- callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_busy));
+ callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_busy), event.getLocalRenderer());
delayedFinish(BUSY_SIGNAL_DELAY_FINISH);
}
@@ -240,12 +247,12 @@ public class WebRtcCallActivity extends Activity {
}
private void handleRecipientUnavailable(@NonNull WebRtcViewModel event) {
- callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_recipient_unavailable));
+ callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_recipient_unavailable), event.getLocalRenderer());
delayedFinish();
}
private void handleServerFailure(@NonNull WebRtcViewModel event) {
- callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_network_failed));
+ callScreen.setActiveCall(event.getRecipient(), getString(R.string.RedPhone_network_failed), event.getLocalRenderer());
delayedFinish();
}
@@ -259,13 +266,13 @@ public class WebRtcCallActivity extends Activity {
dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- WebRtcCallActivity.this.handleTerminate(event.getRecipient());
+ WebRtcCallActivity.this.handleTerminate(event.getRecipient(), event.getLocalRenderer());
}
});
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
- WebRtcCallActivity.this.handleTerminate(event.getRecipient());
+ WebRtcCallActivity.this.handleTerminate(event.getRecipient(), event.getLocalRenderer());
}
});
dialog.show();
@@ -294,7 +301,7 @@ public class WebRtcCallActivity extends Activity {
callScreen.setCancelIdentityButton(new View.OnClickListener() {
@Override
public void onClick(View v) {
- handleTerminate(recipient);
+ handleTerminate(recipient, event.getLocalRenderer());
}
});
}
@@ -316,22 +323,27 @@ public class WebRtcCallActivity extends Activity {
Log.i(TAG, "Got message from service: " + event);
switch (event.getState()) {
- case CALL_CONNECTED: handleCallConnected(event); break;
- case NETWORK_FAILURE: handleServerFailure(event); break;
- case CALL_RINGING: handleCallRinging(event); break;
- case CALL_DISCONNECTED: handleTerminate(event.getRecipient()); break;
- case NO_SUCH_USER: handleNoSuchUser(event); break;
- case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break;
- case CALL_INCOMING: handleIncomingCall(event); break;
- case CALL_OUTGOING: handleOutgoingCall(event); break;
- case CALL_BUSY: handleCallBusy(event); break;
- case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break;
+ case CALL_CONNECTED: handleCallConnected(event); break;
+ case NETWORK_FAILURE: handleServerFailure(event); break;
+ case CALL_RINGING: handleCallRinging(event); break;
+ case CALL_DISCONNECTED: handleTerminate(event.getRecipient(), event.getLocalRenderer()); break;
+ case NO_SUCH_USER: handleNoSuchUser(event); break;
+ case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break;
+ case CALL_INCOMING: handleIncomingCall(event); break;
+ case CALL_OUTGOING: handleOutgoingCall(event); break;
+ case CALL_BUSY: handleCallBusy(event); break;
+ case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break;
}
callScreen.setRemoteVideoEnabled(event.isRemoteVideoEnabled());
callScreen.updateAudioState(event.isBluetoothAvailable(), event.isMicrophoneEnabled());
callScreen.setControlsEnabled(event.getState() != WebRtcViewModel.State.CALL_INCOMING);
- callScreen.setLocalVideoState(event.getLocalCameraState());
+ callScreen.setLocalVideoState(event.getLocalCameraState(), event.getLocalRenderer());
+
+ if (event.getLocalCameraState().getCameraCount() > 0 && enableVideoIfAvailable) {
+ enableVideoIfAvailable = false;
+ handleSetMuteVideo(false);
+ }
}
private class HangupButtonListener implements WebRtcCallScreen.HangupButtonListener {
diff --git a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java
index 3906ce9b11..4f5631ced0 100644
--- a/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java
+++ b/src/org/thoughtcrime/securesms/components/webrtc/WebRtcCallScreen.java
@@ -40,6 +40,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -67,6 +68,7 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
private SurfaceViewRenderer localRenderer;
private PercentFrameLayout localRenderLayout;
private PercentFrameLayout remoteRenderLayout;
+ private PercentFrameLayout localLargeRenderLayout;
private TextView name;
private TextView phoneNumber;
private TextView label;
@@ -83,9 +85,8 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
private WebRtcAnswerDeclineButton incomingCallButton;
- private LiveRecipient recipient;
- private boolean minimized;
-
+ private LiveRecipient recipient;
+ private boolean minimized;
public WebRtcCallScreen(Context context) {
super(context);
@@ -110,8 +111,9 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
endCallButton.show();
}
- public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message) {
+ public void setActiveCall(@NonNull Recipient personInfo, @NonNull String message, @NonNull SurfaceViewRenderer localRenderer) {
setCard(personInfo, message);
+ setRinging(localRenderer);
incomingCallButton.stopRingingAnimation();
incomingCallButton.setVisibility(View.GONE);
endCallButton.show();
@@ -194,22 +196,22 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
this.controls.setControlsEnabled(enabled);
}
- public void setLocalVideoState(@NonNull CameraState cameraState) {
+ public void setLocalVideoState(@NonNull CameraState cameraState, @NonNull SurfaceViewRenderer localRenderer) {
this.controls.setVideoAvailable(cameraState.getCameraCount() > 0);
this.controls.setVideoEnabled(cameraState.isEnabled());
this.controls.setCameraFlipAvailable(cameraState.getCameraCount() > 1);
this.controls.setCameraFlipClickable(cameraState.getActiveDirection() != CameraState.Direction.PENDING);
this.controls.setCameraFlipButtonEnabled(cameraState.getActiveDirection() == CameraState.Direction.BACK);
- if (this.localRenderer != null) {
- this.localRenderer.setMirror(cameraState.getActiveDirection() == CameraState.Direction.FRONT);
+ localRenderer.setMirror(cameraState.getActiveDirection() == CameraState.Direction.FRONT);
+
+ if (localRenderLayout.getChildCount() != 0) {
+ displayLocalRendererInSmallLayout(!cameraState.isEnabled());
+ } else {
+ displayLocalRendererInLargeLayout(!cameraState.isEnabled());
}
- if (this.localRenderLayout.isHidden() == cameraState.isEnabled()) {
- this.localRenderLayout.setHidden(!cameraState.isEnabled());
- this.localRenderLayout.requestLayout();
- this.localRenderer.setVisibility(cameraState.isEnabled() ? VISIBLE : INVISIBLE);
- }
+ localRenderer.setVisibility(cameraState.isEnabled() ? VISIBLE : INVISIBLE);
}
public void setRemoteVideoEnabled(boolean enabled) {
@@ -233,6 +235,42 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
return controls.isVideoEnabled();
}
+ private void displayLocalRendererInLargeLayout(boolean hide) {
+ if (localLargeRenderLayout.getChildCount() == 0) {
+ localRenderLayout.removeAllViews();
+ localLargeRenderLayout.addView(localRenderer);
+ }
+
+ localRenderLayout.setHidden(true);
+ localRenderLayout.requestLayout();
+
+ localLargeRenderLayout.setHidden(hide);
+ localLargeRenderLayout.requestLayout();
+
+ if (hide) {
+ photo.setVisibility(View.VISIBLE);
+ } else {
+ photo.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private void displayLocalRendererInSmallLayout(boolean hide) {
+ if (localRenderLayout.getChildCount() == 0) {
+ localLargeRenderLayout.removeAllViews();
+ localRenderLayout.addView(localRenderer);
+ }
+
+ localLargeRenderLayout.setHidden(true);
+ localLargeRenderLayout.requestLayout();
+
+ localRenderLayout.setHidden(hide);
+ localRenderLayout.requestLayout();
+
+ if (remoteRenderLayout.isHidden()) {
+ photo.setVisibility(View.VISIBLE);
+ }
+ }
+
private void initialize() {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.webrtc_call_screen, this, true);
@@ -241,6 +279,7 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
this.photo = findViewById(R.id.photo);
this.localRenderLayout = findViewById(R.id.local_render_layout);
this.remoteRenderLayout = findViewById(R.id.remote_render_layout);
+ this.localLargeRenderLayout = findViewById(R.id.local_large_render_layout);
this.phoneNumber = findViewById(R.id.phoneNumber);
this.name = findViewById(R.id.name);
this.label = findViewById(R.id.label);
@@ -266,10 +305,28 @@ public class WebRtcCallScreen extends FrameLayout implements RecipientForeverObs
});
}
- private void setConnected(SurfaceViewRenderer localRenderer,
- SurfaceViewRenderer remoteRenderer)
- {
- if (localRenderLayout.getChildCount() == 0 && remoteRenderLayout.getChildCount() == 0) {
+ private void setRinging(SurfaceViewRenderer localRenderer) {
+ if (localLargeRenderLayout.getChildCount() == 0) {
+ if (localRenderer.getParent() != null) {
+ ((ViewGroup)localRenderer.getParent()).removeView(localRenderer);
+ }
+
+ localLargeRenderLayout.setPosition(0, 0, 100, 100);
+
+ localRenderer.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ localRenderer.setMirror(true);
+ localRenderer.setZOrderMediaOverlay(true);
+
+ localLargeRenderLayout.addView(localRenderer);
+
+ this.localRenderer = localRenderer;
+ }
+ }
+
+ private void setConnected(SurfaceViewRenderer localRenderer, SurfaceViewRenderer remoteRenderer) {
+ if (localRenderLayout.getChildCount() == 0) {
if (localRenderer.getParent() != null) {
((ViewGroup)localRenderer.getParent()).removeView(localRenderer);
}
diff --git a/src/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java b/src/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java
index 3b2680bbf7..8805f7bfd3 100644
--- a/src/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java
+++ b/src/org/thoughtcrime/securesms/contactshare/ContactFieldAdapter.java
@@ -142,7 +142,7 @@ class ContactFieldAdapter extends RecyclerView.Adapter 0) {
inflater.inflate(R.menu.conversation_expiring_on, menu);
-
- final MenuItem item = menu.findItem(R.id.menu_expiring_messages);
- final View actionView = MenuItemCompat.getActionView(item);
- final TextView badgeView = actionView.findViewById(R.id.expiration_badge);
-
- badgeView.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(this, recipient.get().getExpireMessages()));
- actionView.setOnClickListener(v -> onOptionsItemSelected(item));
+ titleView.showExpiring(recipient);
} else {
inflater.inflate(R.menu.conversation_expiring_off, menu);
+ titleView.clearExpiring();
}
}
@@ -729,8 +724,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
if (recipient != null && recipient.get().isLocalNumber()) {
- if (isSecureText) menu.findItem(R.id.menu_call_secure).setVisible(false);
- else menu.findItem(R.id.menu_call_insecure).setVisible(false);
+ if (isSecureText) {
+ menu.findItem(R.id.menu_call_secure).setVisible(false);
+ menu.findItem(R.id.menu_video_secure).setVisible(false);
+ } else {
+ menu.findItem(R.id.menu_call_insecure).setVisible(false);
+ }
MenuItem muteItem = menu.findItem(R.id.menu_mute_notifications);
@@ -798,6 +797,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_call_secure: handleDial(getRecipient(), true); return true;
+ case R.id.menu_video_secure: handleVideo(getRecipient()); return true;
case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true;
case R.id.menu_view_media: handleViewMedia(); return true;
case R.id.menu_add_shortcut: handleAddShortcut(); return true;
@@ -1135,6 +1135,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
+ private void handleVideo(final Recipient recipient) {
+ if (recipient == null) return;
+
+ CommunicationActions.startVideoCall(this, recipient);
+ }
+
private void handleDisplayGroupRecipients() {
new GroupMembersDialog(this, getRecipient()).display();
}
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationTitleView.java b/src/org/thoughtcrime/securesms/conversation/ConversationTitleView.java
index 3c80611e45..f406a7d65a 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationTitleView.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationTitleView.java
@@ -18,7 +18,9 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.mms.GlideRequests;
+import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@@ -38,6 +40,8 @@ public class ConversationTitleView extends RelativeLayout {
private ImageView verified;
private View subtitleContainer;
private View verifiedSubtitle;
+ private View expirationBadgeContainer;
+ private TextView expirationBadgeTime;
public ConversationTitleView(Context context) {
this(context, null);
@@ -45,25 +49,37 @@ public class ConversationTitleView extends RelativeLayout {
public ConversationTitleView(Context context, AttributeSet attrs) {
super(context, attrs);
-
}
@Override
public void onFinishInflate() {
super.onFinishInflate();
- this.content = ViewUtil.findById(this, R.id.content);
- this.title = ViewUtil.findById(this, R.id.title);
- this.subtitle = ViewUtil.findById(this, R.id.subtitle);
- this.verified = ViewUtil.findById(this, R.id.verified_indicator);
- this.subtitleContainer = ViewUtil.findById(this, R.id.subtitle_container);
- this.verifiedSubtitle = ViewUtil.findById(this, R.id.verified_subtitle);
- this.avatar = ViewUtil.findById(this, R.id.contact_photo_image);
+ this.content = ViewUtil.findById(this, R.id.content);
+ this.title = ViewUtil.findById(this, R.id.title);
+ this.subtitle = ViewUtil.findById(this, R.id.subtitle);
+ this.verified = ViewUtil.findById(this, R.id.verified_indicator);
+ this.subtitleContainer = ViewUtil.findById(this, R.id.subtitle_container);
+ this.verifiedSubtitle = ViewUtil.findById(this, R.id.verified_subtitle);
+ this.avatar = ViewUtil.findById(this, R.id.contact_photo_image);
+ this.expirationBadgeContainer = ViewUtil.findById(this, R.id.expiration_badge_container);
+ this.expirationBadgeTime = ViewUtil.findById(this, R.id.expiration_badge);
ViewUtil.setTextViewGravityStart(this.title, getContext());
ViewUtil.setTextViewGravityStart(this.subtitle, getContext());
}
+ public void showExpiring(@NonNull LiveRecipient recipient) {
+ expirationBadgeTime.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(getContext(), recipient.get().getExpireMessages()));
+ expirationBadgeContainer.setVisibility(View.VISIBLE);
+ updateSubtitleVisibility();
+ }
+
+ public void clearExpiring() {
+ expirationBadgeContainer.setVisibility(View.GONE);
+ updateSubtitleVisibility();
+ }
+
public void setTitle(@NonNull GlideRequests glideRequests, @Nullable Recipient recipient) {
this.subtitleContainer.setVisibility(View.VISIBLE);
@@ -106,7 +122,7 @@ public class ConversationTitleView extends RelativeLayout {
private void setComposeTitle() {
this.title.setText(R.string.ConversationActivity_compose_message);
this.subtitle.setText(null);
- this.subtitle.setVisibility(View.GONE);
+ updateSubtitleVisibility();
}
private void setRecipientTitle(Recipient recipient) {
@@ -128,11 +144,11 @@ public class ConversationTitleView extends RelativeLayout {
if (TextUtils.isEmpty(recipient.getProfileName())) {
this.subtitle.setText(null);
- this.subtitle.setVisibility(View.GONE);
} else {
this.subtitle.setText("~" + recipient.getProfileName());
- this.subtitle.setVisibility(View.VISIBLE);
}
+
+ updateSubtitleVisibility();
}
private void setContactRecipientTitle(Recipient recipient) {
@@ -140,11 +156,11 @@ public class ConversationTitleView extends RelativeLayout {
if (TextUtils.isEmpty(recipient.getCustomLabel())) {
this.subtitle.setText(null);
- this.subtitle.setVisibility(View.GONE);
} else {
this.subtitle.setText(recipient.getCustomLabel());
- this.subtitle.setVisibility(View.VISIBLE);
}
+
+ updateSubtitleVisibility();
}
private void setGroupRecipientTitle(Recipient recipient) {
@@ -161,7 +177,7 @@ public class ConversationTitleView extends RelativeLayout {
.map(r -> r.toShortString(getContext()))
.collect(Collectors.joining(", ")));
- this.subtitle.setVisibility(View.VISIBLE);
+ updateSubtitleVisibility();
}
private void setSelfTitle() {
@@ -173,10 +189,15 @@ public class ConversationTitleView extends RelativeLayout {
final String displayName = recipient.getDisplayName(getContext());
this.title.setText(displayName);
this.subtitle.setText(null);
- this.subtitle.setVisibility(View.GONE);
+ updateVerifiedSubtitleVisibility();
}
private void updateVerifiedSubtitleVisibility() {
verifiedSubtitle.setVisibility(subtitle.getVisibility() != VISIBLE && verified.getVisibility() == VISIBLE ? VISIBLE : GONE);
}
+
+ private void updateSubtitleVisibility() {
+ subtitle.setVisibility(expirationBadgeContainer.getVisibility() != VISIBLE && !TextUtils.isEmpty(subtitle.getText()) ? VISIBLE : GONE);
+ updateVerifiedSubtitleVisibility();
+ }
}
diff --git a/src/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java b/src/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java
index b8707fd605..727e4ae5b0 100644
--- a/src/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java
+++ b/src/org/thoughtcrime/securesms/preferences/widgets/ContactPreference.java
@@ -16,6 +16,7 @@ public class ContactPreference extends Preference {
private ImageView messageButton;
private ImageView callButton;
private ImageView secureCallButton;
+ private ImageView secureVideoButton;
private Listener listener;
private boolean secure;
@@ -48,9 +49,10 @@ public class ContactPreference extends Preference {
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
- this.messageButton = (ImageView) view.findViewById(R.id.message);
- this.callButton = (ImageView) view.findViewById(R.id.call);
- this.secureCallButton = (ImageView) view.findViewById(R.id.secure_call);
+ this.messageButton = (ImageView) view.findViewById(R.id.message);
+ this.callButton = (ImageView) view.findViewById(R.id.call);
+ this.secureCallButton = (ImageView) view.findViewById(R.id.secure_call);
+ this.secureVideoButton = (ImageView) view.findViewById(R.id.secure_video);
if (listener != null) setListener(listener);
setSecure(secure);
@@ -59,8 +61,9 @@ public class ContactPreference extends Preference {
public void setSecure(boolean secure) {
this.secure = secure;
- if (secureCallButton != null) secureCallButton.setVisibility(secure ? View.VISIBLE : View.GONE);
- if (callButton != null) callButton.setVisibility(secure ? View.GONE : View.VISIBLE);
+ if (secureCallButton != null) secureCallButton.setVisibility(secure ? View.VISIBLE : View.GONE);
+ if (secureVideoButton != null) secureVideoButton.setVisibility(secure ? View.VISIBLE : View.GONE);
+ if (callButton != null) callButton.setVisibility(secure ? View.GONE : View.VISIBLE);
int color;
@@ -70,22 +73,25 @@ public class ContactPreference extends Preference {
color = getContext().getResources().getColor(R.color.grey_600);
}
- if (messageButton != null) messageButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- if (secureCallButton != null) secureCallButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
- if (callButton != null) callButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ if (messageButton != null) messageButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ if (secureCallButton != null) secureCallButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ if (secureVideoButton != null) secureVideoButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ if (callButton != null) callButton.setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
public void setListener(Listener listener) {
this.listener = listener;
- if (this.messageButton != null) this.messageButton.setOnClickListener(v -> listener.onMessageClicked());
- if (this.secureCallButton != null) this.secureCallButton.setOnClickListener(v -> listener.onSecureCallClicked());
- if (this.callButton != null) this.callButton.setOnClickListener(v -> listener.onInSecureCallClicked());
+ if (this.messageButton != null) this.messageButton.setOnClickListener(v -> listener.onMessageClicked());
+ if (this.secureCallButton != null) this.secureCallButton.setOnClickListener(v -> listener.onSecureCallClicked());
+ if (this.secureVideoButton != null) this.secureVideoButton.setOnClickListener(v -> listener.onSecureVideoClicked());
+ if (this.callButton != null) this.callButton.setOnClickListener(v -> listener.onInSecureCallClicked());
}
public interface Listener {
public void onMessageClicked();
public void onSecureCallClicked();
+ public void onSecureVideoClicked();
public void onInSecureCallClicked();
}
diff --git a/src/org/thoughtcrime/securesms/util/CommunicationActions.java b/src/org/thoughtcrime/securesms/util/CommunicationActions.java
index cd8237c386..53ef04b132 100644
--- a/src/org/thoughtcrime/securesms/util/CommunicationActions.java
+++ b/src/org/thoughtcrime/securesms/util/CommunicationActions.java
@@ -9,6 +9,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.app.TaskStackBuilder;
import android.text.TextUtils;
import android.widget.Toast;
@@ -27,29 +28,34 @@ public class CommunicationActions {
if (TelephonyUtil.isAnyPstnLineBusy(activity)) {
Toast.makeText(activity,
R.string.CommunicationActions_a_cellular_call_is_already_in_progress,
- Toast.LENGTH_SHORT
- ).show();
+ Toast.LENGTH_SHORT)
+ .show();
return;
}
- Permissions.with(activity)
- .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
- .ifNecessary()
- .withRationaleDialog(activity.getString(R.string.ConversationActivity_to_call_s_signal_needs_access_to_your_microphone_and_camera, recipient.toShortString(activity)),
- R.drawable.ic_mic_solid_24,
- R.drawable.ic_videocam_white_48dp)
- .withPermanentDenialDialog(activity.getString(R.string.ConversationActivity_signal_needs_the_microphone_and_camera_permissions_in_order_to_call_s, recipient.toShortString(activity)))
- .onAllGranted(() -> {
- Intent intent = new Intent(activity, WebRtcCallService.class);
- intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL);
- intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, recipient.getId());
- activity.startService(intent);
+ new AlertDialog.Builder(activity)
+ .setMessage(R.string.CommunicationActions_start_voice_call)
+ .setPositiveButton(R.string.CommunicationActions_call, (d, w) -> startCallInternal(activity, recipient, false))
+ .setNegativeButton(R.string.CommunicationActions_cancel, (d, w) -> d.dismiss())
+ .setCancelable(true)
+ .show();
+ }
- Intent activityIntent = new Intent(activity, WebRtcCallActivity.class);
- activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- activity.startActivity(activityIntent);
- })
- .execute();
+ public static void startVideoCall(@NonNull Activity activity, @NonNull Recipient recipient) {
+ if (TelephonyUtil.isAnyPstnLineBusy(activity)) {
+ Toast.makeText(activity,
+ R.string.CommunicationActions_a_cellular_call_is_already_in_progress,
+ Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ new AlertDialog.Builder(activity)
+ .setMessage(R.string.CommunicationActions_start_video_call)
+ .setPositiveButton(R.string.CommunicationActions_call, (d, w) -> startCallInternal(activity, recipient, true))
+ .setNegativeButton(R.string.CommunicationActions_cancel, (d, w) -> d.dismiss())
+ .setCancelable(true)
+ .show();
}
public static void startConversation(@NonNull Context context, @NonNull Recipient recipient, @Nullable String text) {
@@ -103,4 +109,31 @@ public class CommunicationActions {
Toast.makeText(context, R.string.CommunicationActions_no_browser_found, Toast.LENGTH_SHORT).show();
}
}
+
+
+ private static void startCallInternal(@NonNull Activity activity, @NonNull Recipient recipient, boolean isVideo) {
+ Permissions.with(activity)
+ .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
+ .ifNecessary()
+ .withRationaleDialog(activity.getString(R.string.ConversationActivity_to_call_s_signal_needs_access_to_your_microphone_and_camera, recipient.getDisplayName(activity)),
+ R.drawable.ic_mic_solid_24,
+ R.drawable.ic_video_solid_24_tinted)
+ .withPermanentDenialDialog(activity.getString(R.string.ConversationActivity_signal_needs_the_microphone_and_camera_permissions_in_order_to_call_s, recipient.getDisplayName(activity)))
+ .onAllGranted(() -> {
+ Intent intent = new Intent(activity, WebRtcCallService.class);
+ intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL);
+ intent.putExtra(WebRtcCallService.EXTRA_REMOTE_RECIPIENT, recipient.getId());
+ activity.startService(intent);
+
+ Intent activityIntent = new Intent(activity, WebRtcCallActivity.class);
+ activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (isVideo) {
+ activityIntent.putExtra(WebRtcCallActivity.EXTRA_ENABLE_VIDEO_IF_AVAILABLE, true);
+ }
+
+ activity.startActivity(activityIntent);
+ })
+ .execute();
+ }
}