浏览代码

improve call screens, add outgoing ringtone

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 4 年之前
父节点
当前提交
642146de02

+ 7 - 0
README.md

@@ -84,5 +84,12 @@ commit automatically with `git commit -s`. You can also use git [aliases](https:
 like `git config --global alias.ci 'commit -s'`. Now you can commit with
 `git ci` and the commit will be signed.
 
+## Credits
+
+### Ringtones
+
+- [Ringtones by Librem](https://soundcloud.com/feandesign/sets/librem-5-sounds)
+- [Telefon-Freiton in Deutschland nach DTAG 1 TR 110-1, Kap. 8.3](https://commons.wikimedia.org/wiki/File:1TR110-1_Kap8.3_Freiton1.ogg)
+
 [dcofile]: https://github.com/nextcloud/talk-android/blob/master/contribute/developer-certificate-of-origin
 [applyalicense]: https://github.com/nextcloud/talk-android/blob/master/contribute/HowToApplyALicense.md

+ 0 - 3
app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt

@@ -40,9 +40,6 @@ import com.nextcloud.talk.controllers.CallNotificationController
 import com.nextcloud.talk.controllers.ChatController
 import com.nextcloud.talk.events.ConfigurationChangeEvent
 import com.nextcloud.talk.utils.bundle.BundleKeys
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
 
 @AutoInjector(NextcloudTalkApplication::class)
 class MagicCallActivity : BaseActivity() {

+ 3 - 1
app/src/main/java/com/nextcloud/talk/api/NcApi.java

@@ -177,8 +177,10 @@ public interface NcApi {
         Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken
     */
 
+    @FormUrlEncoded
     @POST
-    Observable<GenericOverall> joinCall(@Nullable @Header("Authorization") String authorization, @Url String url);
+    Observable<GenericOverall> joinCall(@Nullable @Header("Authorization") String authorization, @Url String url,
+                                        @Field("flags") Integer inCall);
 
     /*
     Server URL is: baseUrl + ocsApiVersion + spreedApiVersion + /call/callToken

+ 186 - 91
app/src/main/java/com/nextcloud/talk/controllers/CallController.java

@@ -46,6 +46,10 @@ import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
 import com.bluelinelabs.logansquare.LoganSquare;
 import com.facebook.drawee.backends.pipeline.Fresco;
 import com.facebook.drawee.interfaces.DraweeController;
@@ -136,9 +140,6 @@ import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
 import autodagger.AutoInjector;
 import butterknife.BindView;
 import butterknife.OnClick;
@@ -177,24 +178,31 @@ public class CallController extends BaseController {
 
     @BindView(R.id.pip_video_view)
     SurfaceViewRenderer pipVideoView;
-    @BindView(R.id.relative_layout)
-    RelativeLayout relativeLayout;
+    @BindView(R.id.controllerCallLayout)
+    RelativeLayout controllerCallLayout;
     @BindView(R.id.remote_renderers_layout)
     LinearLayout remoteRenderersLayout;
 
-    @BindView(R.id.callControlsRelativeLayout)
-    RelativeLayout callControls;
+    @BindView(R.id.callControlsLinearLayout)
+    LinearLayout callControls;
     @BindView(R.id.call_control_microphone)
     SimpleDraweeView microphoneControlButton;
     @BindView(R.id.call_control_camera)
     SimpleDraweeView cameraControlButton;
     @BindView(R.id.call_control_switch_camera)
     SimpleDraweeView cameraSwitchButton;
-    @BindView(R.id.connectingTextView)
-    TextView connectingTextView;
+    @BindView(R.id.callStateTextView)
+    TextView callStateTextView;
+
+    @BindView(R.id.callInfosLinearLayout)
+    LinearLayout callInfosLinearLayout;
+    @BindView(R.id.callVoiceOrVideoTextView)
+    TextView callVoiceOrVideoTextView;
+    @BindView(R.id.callConversationNameTextView)
+    TextView callConversationNameTextView;
 
-    @BindView(R.id.connectingRelativeLayoutView)
-    RelativeLayout connectingView;
+    @BindView(R.id.callStateRelativeLayoutView)
+    RelativeLayout callStateView;
 
     @BindView(R.id.conversationRelativeLayoutView)
     RelativeLayout conversationView;
@@ -202,7 +210,7 @@ public class CallController extends BaseController {
     @BindView(R.id.errorImageView)
     ImageView errorImageView;
 
-    @BindView(R.id.progress_bar)
+    @BindView(R.id.callStateProgressBar)
     ProgressBar progressBar;
 
     @Inject
@@ -234,6 +242,7 @@ public class CallController extends BaseController {
     private CameraEnumerator cameraEnumerator;
     private String roomToken;
     private UserEntity conversationUser;
+    private String conversationName;
     private String callSession;
     private MediaStream localMediaStream;
     private String credentials;
@@ -247,9 +256,12 @@ public class CallController extends BaseController {
     private boolean needsPing = true;
 
     private boolean isVoiceOnlyCall;
+    private boolean isIncomingCallFromNotification;
     private Handler callControlHandler = new Handler();
+    private Handler callInfosHandler = new Handler();
     private Handler cameraSwitchHandler = new Handler();
 
+    // push to talk
     private boolean isPTTActive = false;
     private PulseAnimation pulseAnimation;
     private View.OnClickListener videoOnClickListener;
@@ -276,7 +288,7 @@ public class CallController extends BaseController {
 
     @Parcel
     public enum CallStatus {
-        CALLING, CALLING_TIMEOUT, ESTABLISHED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED
+        CONNECTING, CALLING_TIMEOUT, JOINED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED
     }
 
     public CallController(Bundle args) {
@@ -287,8 +299,13 @@ public class CallController extends BaseController {
         roomToken = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), "");
         conversationUser = args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY());
         conversationPassword = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), "");
+        conversationName = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), "");
         isVoiceOnlyCall = args.getBoolean(BundleKeys.INSTANCE.getKEY_CALL_VOICE_ONLY(), false);
 
+        if (args.containsKey(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL())) {
+            isIncomingCallFromNotification = args.getBoolean(BundleKeys.INSTANCE.getKEY_FROM_NOTIFICATION_START_CALL());
+        }
+
         credentials = ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken());
 
         baseUrl = args.getString(BundleKeys.INSTANCE.getKEY_MODIFIED_BASE_URL(), "");
@@ -298,11 +315,11 @@ public class CallController extends BaseController {
         }
 
         powerManagerUtils = new PowerManagerUtils();
-        
+
         if (args.getString("state", "").equalsIgnoreCase("resume")) {
             setCallState(CallStatus.IN_CONVERSATION);
         } else {
-            setCallState(CallStatus.CALLING);
+            setCallState(CallStatus.CONNECTING);
         }
     }
 
@@ -362,7 +379,7 @@ public class CallController extends BaseController {
         //Create a new PeerConnectionFactory instance.
         PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
         DefaultVideoEncoderFactory defaultVideoEncoderFactory = new DefaultVideoEncoderFactory(
-                rootEglBase.getEglBaseContext(),true,true);
+                rootEglBase.getEglBaseContext(), true, true);
         DefaultVideoDecoderFactory defaultVideoDecoderFactory = new DefaultVideoDecoderFactory(rootEglBase.getEglBaseContext());
 
         peerConnectionFactory = PeerConnectionFactory.builder()
@@ -456,7 +473,12 @@ public class CallController extends BaseController {
             cameraSwitchButton.setVisibility(View.GONE);
             cameraControlButton.setVisibility(View.GONE);
             pipVideoView.setVisibility(View.GONE);
+
+            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+            params.addRule(RelativeLayout.BELOW, R.id.callInfosLinearLayout);
+            remoteRenderersLayout.setLayoutParams(params);
         } else {
+            callControlEnableSpeaker.setVisibility(View.GONE);
             if (cameraEnumerator.getDeviceNames().length < 2) {
                 cameraSwitchButton.setVisibility(View.GONE);
             }
@@ -484,7 +506,7 @@ public class CallController extends BaseController {
     }
 
     private boolean isConnectionEstablished() {
-        return (currentCallStatus.equals(CallStatus.ESTABLISHED) || currentCallStatus.equals(CallStatus.IN_CONVERSATION));
+        return (currentCallStatus.equals(CallStatus.JOINED) || currentCallStatus.equals(CallStatus.IN_CONVERSATION));
     }
 
     @AfterPermissionGranted(100)
@@ -648,6 +670,7 @@ public class CallController extends BaseController {
     boolean onMicrophoneLongClick() {
         if (!audioOn) {
             callControlHandler.removeCallbacksAndMessages(null);
+            callInfosHandler.removeCallbacksAndMessages(null);
             cameraSwitchHandler.removeCallbacksAndMessages(null);
             isPTTActive = true;
             callControls.setVisibility(View.VISIBLE);
@@ -735,7 +758,7 @@ public class CallController extends BaseController {
             }
         }
     }
-    
+
     @OnClick(R.id.callControlToggleChat)
     void onToggleChatClick() {
         ((MagicCallActivity) getActivity()).showChat();
@@ -868,6 +891,7 @@ public class CallController extends BaseController {
 
             if (show) {
                 callControlHandler.removeCallbacksAndMessages(null);
+                callInfosHandler.removeCallbacksAndMessages(null);
                 cameraSwitchHandler.removeCallbacksAndMessages(null);
                 alpha = 1.0f;
                 duration = 1000;
@@ -875,8 +899,13 @@ public class CallController extends BaseController {
                     callControls.setAlpha(0.0f);
                     callControls.setVisibility(View.VISIBLE);
 
+                    callInfosLinearLayout.setAlpha(0.0f);
+                    callInfosLinearLayout.setVisibility(View.VISIBLE);
+
                     cameraSwitchButton.setAlpha(0.0f);
-                    cameraSwitchButton.setVisibility(View.VISIBLE);
+                    if (videoOn) {
+                        cameraSwitchButton.setVisibility(View.VISIBLE);
+                    }
                 } else {
                     callControlHandler.postDelayed(() -> animateCallControls(false, 0), 5000);
                     return;
@@ -920,6 +949,37 @@ public class CallController extends BaseController {
                         });
             }
 
+            if (callInfosLinearLayout != null) {
+                callInfosLinearLayout.setEnabled(false);
+                callInfosLinearLayout.animate()
+                        .translationY(0)
+                        .alpha(alpha)
+                        .setDuration(duration)
+                        .setStartDelay(startDelay)
+                        .setListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                super.onAnimationEnd(animation);
+                                if (callInfosLinearLayout != null) {
+                                    if (!show) {
+                                        callInfosLinearLayout.setVisibility(View.GONE);
+                                    } else {
+                                        callInfosHandler.postDelayed(new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                if (!isPTTActive) {
+                                                    animateCallControls(false, 0);
+                                                }
+                                            }
+                                        }, 7500);
+                                    }
+
+                                    callInfosLinearLayout.setEnabled(true);
+                                }
+                            }
+                        });
+            }
+
             if (cameraSwitchButton != null) {
                 cameraSwitchButton.setEnabled(false);
                 cameraSwitchButton.animate()
@@ -1139,8 +1199,15 @@ public class CallController extends BaseController {
     }
 
     private void performCall() {
+        Integer inCallFlag;
+        if (isVoiceOnlyCall) {
+            inCallFlag = (int) Participant.ParticipantFlags.IN_CALL_WITH_AUDIO.getValue();
+        } else {
+            inCallFlag = (int) Participant.ParticipantFlags.IN_CALL_WITH_AUDIO_AND_VIDEO.getValue();
+        }
+
         ncApi.joinCall(credentials,
-                ApiUtils.getUrlForCall(baseUrl, roomToken))
+                ApiUtils.getUrlForCall(baseUrl, roomToken), inCallFlag)
                 .subscribeOn(Schedulers.io())
                 .retry(3)
                 .observeOn(AndroidSchedulers.mainThread())
@@ -1153,7 +1220,7 @@ public class CallController extends BaseController {
                     @Override
                     public void onNext(GenericOverall genericOverall) {
                         if (!currentCallStatus.equals(CallStatus.LEAVING)) {
-                            setCallState(CallStatus.ESTABLISHED);
+                            setCallState(CallStatus.JOINED);
 
                             ApplicationWideCurrentRoomHolder.getInstance().setInCall(true);
 
@@ -1339,7 +1406,7 @@ public class CallController extends BaseController {
     private void receivedSignalingMessage(Signaling signaling) throws IOException {
         String messageType = signaling.getType();
 
-        if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CALLING)) {
+        if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CONNECTING)) {
             return;
         }
 
@@ -1464,38 +1531,38 @@ public class CallController extends BaseController {
     }
 
     private void hangupNetworkCalls(boolean shutDownView) {
-            ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken))
-                    .subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(new Observer<GenericOverall>() {
-                        @Override
-                        public void onSubscribe(Disposable d) {
+        ncApi.leaveCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken))
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<GenericOverall>() {
+                    @Override
+                    public void onSubscribe(Disposable d) {
 
-                        }
+                    }
 
-                        @Override
-                        public void onNext(GenericOverall genericOverall) {
-                            if (isMultiSession) {
-                                if (shutDownView && getActivity() != null) {
-                                    getActivity().finish();
-                                } else if (!shutDownView && (currentCallStatus.equals(CallStatus.RECONNECTING) || currentCallStatus.equals(CallStatus.PUBLISHER_FAILED))) {
-                                    initiateCall();
-                                }
-                            } else {
-                                leaveRoom(shutDownView);
+                    @Override
+                    public void onNext(GenericOverall genericOverall) {
+                        if (isMultiSession) {
+                            if (shutDownView && getActivity() != null) {
+                                getActivity().finish();
+                            } else if (!shutDownView && (currentCallStatus.equals(CallStatus.RECONNECTING) || currentCallStatus.equals(CallStatus.PUBLISHER_FAILED))) {
+                                initiateCall();
                             }
+                        } else {
+                            leaveRoom(shutDownView);
                         }
+                    }
 
-                        @Override
-                        public void onError(Throwable e) {
+                    @Override
+                    public void onError(Throwable e) {
 
-                        }
+                    }
 
-                        @Override
-                        public void onComplete() {
+                    @Override
+                    public void onComplete() {
 
-                        }
-                    });
+                    }
+                });
     }
 
     private void leaveRoom(boolean shutDownView) {
@@ -1567,7 +1634,7 @@ public class CallController extends BaseController {
         // Calculate sessions that join the call
         newSessions.removeAll(oldSesssions);
 
-        if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CALLING)) {
+        if (!isConnectionEstablished() && !currentCallStatus.equals(CallStatus.CONNECTING)) {
             return;
         }
 
@@ -1759,7 +1826,7 @@ public class CallController extends BaseController {
                 boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
                         .PeerConnectionEventType.SENSOR_FAR) && videoOn;
                 if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA) &&
-                        (currentCallStatus.equals(CallStatus.CALLING) || isConnectionEstablished()) && videoOn
+                        (currentCallStatus.equals(CallStatus.CONNECTING) || isConnectionEstablished()) && videoOn
                         && enableVideo != localVideoTrack.enabled()) {
                     toggleMedia(enableVideo, true);
                 }
@@ -2066,7 +2133,7 @@ public class CallController extends BaseController {
     private void gotNick(String sessionId, String nick, String type) {
         String remoteRendererTag = sessionId + "+" + type;
 
-        if (relativeLayout != null) {
+        if (controllerCallLayout != null) {
             RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(remoteRendererTag);
             TextView textView = relativeLayout.findViewById(R.id.peer_nick_text_view);
             if (!textView.getText().equals(nick)) {
@@ -2079,7 +2146,7 @@ public class CallController extends BaseController {
         }
     }
 
-    @OnClick(R.id.connectingRelativeLayoutView)
+    @OnClick(R.id.callStateRelativeLayoutView)
     public void onConnectingViewClick() {
         if (currentCallStatus.equals(CallStatus.CALLING_TIMEOUT)) {
             setCallState(CallStatus.RECONNECTING);
@@ -2097,16 +2164,24 @@ public class CallController extends BaseController {
             }
 
             switch (callState) {
-                case CALLING:
+                case CONNECTING:
                     handler.post(() -> {
                         playCallingSound();
-                        connectingTextView.setText(R.string.nc_connecting_call);
-                        if (connectingView.getVisibility() != View.VISIBLE) {
-                            connectingView.setVisibility(View.VISIBLE);
+                        if (isIncomingCallFromNotification) {
+                            callStateTextView.setText(R.string.nc_call_incoming);
+                        } else {
+                            callStateTextView.setText(R.string.nc_call_ringing);
+                        }
+                        callConversationNameTextView.setText(conversationName);
+
+                        callVoiceOrVideoTextView.setText(isVoiceOnlyCall ? R.string.nc_voice_call : R.string.nc_video_call);
+
+                        if (callStateView.getVisibility() != View.VISIBLE) {
+                            callStateView.setVisibility(View.VISIBLE);
                         }
 
-                        if (conversationView.getVisibility() != View.INVISIBLE) {
-                            conversationView.setVisibility(View.INVISIBLE);
+                        if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) {
+                            remoteRenderersLayout.setVisibility(View.INVISIBLE);
                         }
 
                         if (progressBar.getVisibility() != View.VISIBLE) {
@@ -2121,17 +2196,18 @@ public class CallController extends BaseController {
                 case CALLING_TIMEOUT:
                     handler.post(() -> {
                         hangup(false);
-                        connectingTextView.setText(R.string.nc_call_timeout);
-                        if (connectingView.getVisibility() != View.VISIBLE) {
-                            connectingView.setVisibility(View.VISIBLE);
+                        callStateTextView.setText(R.string.nc_call_timeout);
+                        callVoiceOrVideoTextView.setText(isVoiceOnlyCall ? R.string.nc_voice_call : R.string.nc_video_call);
+                        if (callStateView.getVisibility() != View.VISIBLE) {
+                            callStateView.setVisibility(View.VISIBLE);
                         }
 
                         if (progressBar.getVisibility() != View.GONE) {
                             progressBar.setVisibility(View.GONE);
                         }
 
-                        if (conversationView.getVisibility() != View.INVISIBLE) {
-                            conversationView.setVisibility(View.INVISIBLE);
+                        if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) {
+                            remoteRenderersLayout.setVisibility(View.INVISIBLE);
                         }
 
                         errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp);
@@ -2144,12 +2220,13 @@ public class CallController extends BaseController {
                 case RECONNECTING:
                     handler.post(() -> {
                         playCallingSound();
-                        connectingTextView.setText(R.string.nc_call_reconnecting);
-                        if (connectingView.getVisibility() != View.VISIBLE) {
-                            connectingView.setVisibility(View.VISIBLE);
+                        callStateTextView.setText(R.string.nc_call_reconnecting);
+                        callVoiceOrVideoTextView.setText(isVoiceOnlyCall ? R.string.nc_voice_call : R.string.nc_video_call);
+                        if (callStateView.getVisibility() != View.VISIBLE) {
+                            callStateView.setVisibility(View.VISIBLE);
                         }
-                        if (conversationView.getVisibility() != View.INVISIBLE) {
-                            conversationView.setVisibility(View.INVISIBLE);
+                        if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) {
+                            remoteRenderersLayout.setVisibility(View.INVISIBLE);
                         }
                         if (progressBar.getVisibility() != View.VISIBLE) {
                             progressBar.setVisibility(View.VISIBLE);
@@ -2160,13 +2237,18 @@ public class CallController extends BaseController {
                         }
                     });
                     break;
-                case ESTABLISHED:
+                case JOINED:
                     handler.postDelayed(() -> setCallState(CallStatus.CALLING_TIMEOUT), 45000);
                     handler.post(() -> {
-                        if (connectingView != null) {
-                            connectingTextView.setText(R.string.nc_calling);
-                            if (connectingTextView.getVisibility() != View.VISIBLE) {
-                                connectingView.setVisibility(View.VISIBLE);
+                        callVoiceOrVideoTextView.setText(isVoiceOnlyCall ? R.string.nc_voice_call : R.string.nc_video_call);
+                        if (callStateView != null) {
+                            if (isIncomingCallFromNotification) {
+                                callStateTextView.setText(R.string.nc_call_incoming);
+                            } else {
+                                callStateTextView.setText(R.string.nc_call_ringing);
+                            }
+                            if (callStateView.getVisibility() != View.VISIBLE) {
+                                callStateView.setVisibility(View.VISIBLE);
                             }
                         }
 
@@ -2176,9 +2258,9 @@ public class CallController extends BaseController {
                             }
                         }
 
-                        if (conversationView != null) {
-                            if (conversationView.getVisibility() != View.INVISIBLE) {
-                                conversationView.setVisibility(View.INVISIBLE);
+                        if (remoteRenderersLayout != null) {
+                            if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) {
+                                remoteRenderersLayout.setVisibility(View.INVISIBLE);
                             }
                         }
 
@@ -2192,14 +2274,19 @@ public class CallController extends BaseController {
                 case IN_CONVERSATION:
                     handler.post(() -> {
                         stopCallingSound();
+                        callVoiceOrVideoTextView.setText(isVoiceOnlyCall ? R.string.nc_voice_call : R.string.nc_video_call);
+
+                        if (!isVoiceOnlyCall) {
+                            callInfosLinearLayout.setVisibility(View.GONE);
+                        }
 
                         if (!isPTTActive) {
                             animateCallControls(false, 5000);
                         }
 
-                        if (connectingView != null) {
-                            if (connectingView.getVisibility() != View.INVISIBLE) {
-                                connectingView.setVisibility(View.INVISIBLE);
+                        if (callStateView != null) {
+                            if (callStateView.getVisibility() != View.INVISIBLE) {
+                                callStateView.setVisibility(View.INVISIBLE);
                             }
                         }
 
@@ -2209,9 +2296,9 @@ public class CallController extends BaseController {
                             }
                         }
 
-                        if (conversationView != null) {
-                            if (conversationView.getVisibility() != View.VISIBLE) {
-                                conversationView.setVisibility(View.VISIBLE);
+                        if (remoteRenderersLayout != null) {
+                            if (remoteRenderersLayout.getVisibility() != View.VISIBLE) {
+                                remoteRenderersLayout.setVisibility(View.VISIBLE);
                             }
                         }
 
@@ -2226,18 +2313,18 @@ public class CallController extends BaseController {
                     handler.post(() -> {
                         stopCallingSound();
 
-                        if (connectingTextView != null) {
-                            connectingTextView.setText(R.string.nc_offline);
+                        if (callStateTextView != null) {
+                            callStateTextView.setText(R.string.nc_offline);
 
-                            if (connectingView.getVisibility() != View.VISIBLE) {
-                                connectingView.setVisibility(View.VISIBLE);
+                            if (callStateView.getVisibility() != View.VISIBLE) {
+                                callStateView.setVisibility(View.VISIBLE);
                             }
                         }
 
 
-                        if (conversationView != null) {
-                            if (conversationView.getVisibility() != View.INVISIBLE) {
-                                conversationView.setVisibility(View.INVISIBLE);
+                        if (remoteRenderersLayout != null) {
+                            if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) {
+                                remoteRenderersLayout.setVisibility(View.INVISIBLE);
                             }
                         }
 
@@ -2259,9 +2346,10 @@ public class CallController extends BaseController {
                     handler.post(() -> {
                         if (!isDestroyed() && !isBeingDestroyed()) {
                             stopCallingSound();
-                            connectingTextView.setText(R.string.nc_leaving_call);
-                            connectingView.setVisibility(View.VISIBLE);
-                            conversationView.setVisibility(View.INVISIBLE);
+                            callVoiceOrVideoTextView.setText(isVoiceOnlyCall ? R.string.nc_voice_call : R.string.nc_video_call);
+                            callStateTextView.setText(R.string.nc_leaving_call);
+                            callStateView.setVisibility(View.VISIBLE);
+                            remoteRenderersLayout.setVisibility(View.INVISIBLE);
                             progressBar.setVisibility(View.VISIBLE);
                             errorImageView.setVisibility(View.GONE);
                         }
@@ -2274,7 +2362,14 @@ public class CallController extends BaseController {
 
     private void playCallingSound() {
         stopCallingSound();
-        Uri ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/raw/librem_by_feandesign_call");
+        Uri ringtoneUri;
+        if (isIncomingCallFromNotification) {
+            ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
+                    "/raw/librem_by_feandesign_call");
+        } else {
+            ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/raw" +
+                    "/tr110_1_kap8_3_freiton1");
+        }
         if (getActivity() != null) {
             mediaPlayer = new MediaPlayer();
             try {

+ 139 - 85
app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java

@@ -70,6 +70,7 @@ import com.nextcloud.talk.events.ConfigurationChangeEvent;
 import com.nextcloud.talk.models.RingtoneSettings;
 import com.nextcloud.talk.models.database.UserEntity;
 import com.nextcloud.talk.models.json.conversations.Conversation;
+import com.nextcloud.talk.models.json.conversations.RoomOverall;
 import com.nextcloud.talk.models.json.conversations.RoomsOverall;
 import com.nextcloud.talk.models.json.participants.Participant;
 import com.nextcloud.talk.models.json.participants.ParticipantsOverall;
@@ -83,6 +84,7 @@ import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder;
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
 import org.greenrobot.eventbus.ThreadMode;
+import org.jetbrains.annotations.NotNull;
 import org.michaelevans.colorart.library.ColorArt;
 import org.parceler.Parcels;
 
@@ -121,6 +123,9 @@ public class CallNotificationController extends BaseController {
     @Inject
     Context context;
 
+    @BindView(R.id.incomingCallVoiceOrVideoTextView)
+    TextView incomingCallVoiceOrVideoTextView;
+
     @BindView(R.id.conversationNameTextView)
     TextView conversationNameTextView;
 
@@ -197,6 +202,8 @@ public class CallNotificationController extends BaseController {
 
     private void proceedToCall() {
         originalBundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), currentConversation.getToken());
+        originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName());
+        originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName());
 
         getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle))
                 .popChangeHandler(new HorizontalChangeHandler())
@@ -253,38 +260,77 @@ public class CallNotificationController extends BaseController {
     }
 
     private void handleFromNotification() {
-        ncApi.getRooms(credentials, ApiUtils.getUrlForGetRooms(userBeingCalled.getBaseUrl()))
-                .subscribeOn(Schedulers.io())
-                .retry(3)
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(new Observer<RoomsOverall>() {
-                    @Override
-                    public void onSubscribe(Disposable d) {
-                        disposablesList.add(d);
-                    }
+        boolean isConversationApiV3 = userBeingCalled.hasSpreedFeatureCapability("conversation-v3");
+        if(isConversationApiV3) {
+            ncApi.getRoom(credentials, ApiUtils.getRoomV3(userBeingCalled.getBaseUrl(), roomId))
+                    .subscribeOn(Schedulers.io())
+                    .retry(3)
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new Observer<RoomOverall>() {
+                        @Override
+                        public void onSubscribe(Disposable d) {
+                            disposablesList.add(d);
+                        }
 
-                    @Override
-                    public void onNext(RoomsOverall roomsOverall) {
-                        for (Conversation conversation : roomsOverall.getOcs().getData()) {
-                            if (roomId.equals(conversation.getRoomId()) || roomId.equals(conversation.token)) {
-                                currentConversation = conversation;
-                                runAllThings();
-                                break;
+                        @Override
+                        public void onNext(@NotNull RoomOverall roomOverall) {
+                            currentConversation = roomOverall.getOcs().data;
+                            runAllThings();
+
+                            boolean hasCallFlags = userBeingCalled.hasSpreedFeatureCapability("conversation-call-flags");
+                            if (hasCallFlags) {
+                                if (isInCallWithVideo(currentConversation.callFlag)){
+                                    incomingCallVoiceOrVideoTextView.setText(R.string.nc_video_call);
+                                } else {
+                                    incomingCallVoiceOrVideoTextView.setText(R.string.nc_voice_call);
+                                }
                             }
                         }
 
-                    }
+                        @Override
+                        public void onError(Throwable e) {
 
-                    @Override
-                    public void onError(Throwable e) {
+                        }
 
-                    }
+                        @Override
+                        public void onComplete() {
 
-                    @Override
-                    public void onComplete() {
+                        }
+                    });
+        } else {
+            ncApi.getRoom(credentials, ApiUtils.getRoom(userBeingCalled.getBaseUrl(), roomId))
+                    .subscribeOn(Schedulers.io())
+                    .retry(3)
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new Observer<RoomOverall>() {
+                        @Override
+                        public void onSubscribe(Disposable d) {
+                            disposablesList.add(d);
+                        }
 
-                    }
-                });
+                        @SuppressLint("LongLogTag")
+                        @Override
+                        public void onNext(@NotNull RoomOverall roomOverall) {
+                            currentConversation = roomOverall.getOcs().data;
+                            runAllThings();
+                        }
+
+                        @Override
+                        public void onError(Throwable e) {
+
+                        }
+
+                        @Override
+                        public void onComplete() {
+
+                        }
+                    });
+        }
+    }
+
+    private boolean isInCallWithVideo(int callFlag) {
+        return (Participant.ParticipantFlags.IN_CALL_WITH_VIDEO.getValue() == callFlag
+                || Participant.ParticipantFlags.IN_CALL_WITH_AUDIO_AND_VIDEO.getValue() == callFlag);
     }
 
     private void runAllThings() {
@@ -321,73 +367,14 @@ public class CallNotificationController extends BaseController {
         }
 
         if (DoNotDisturbUtils.INSTANCE.shouldPlaySound()) {
-            String callRingtonePreferenceString = appPreferences.getCallRingtoneUri();
-            Uri ringtoneUri;
-
-            if (TextUtils.isEmpty(callRingtonePreferenceString)) {
-                // play default sound
-                ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
-                        "/raw/librem_by_feandesign_call");
-            } else {
-                try {
-                    RingtoneSettings ringtoneSettings = LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class);
-                    ringtoneUri = ringtoneSettings.getRingtoneUri();
-                } catch (IOException e) {
-                    Log.e(TAG, "Failed to parse ringtone settings");
-                    ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
-                            "/raw/librem_by_feandesign_call");
-                }
-            }
-
-            if (ringtoneUri != null && getActivity() != null) {
-                mediaPlayer = new MediaPlayer();
-                try {
-                    mediaPlayer.setDataSource(getActivity(), ringtoneUri);
-
-                    mediaPlayer.setLooping(true);
-                    AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes
-                            .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
-                    mediaPlayer.setAudioAttributes(audioAttributes);
-
-                    mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());
-
-                    mediaPlayer.prepareAsync();
-                } catch (IOException e) {
-                    Log.e(TAG, "Failed to set data source");
-                }
-            }
+            playRingtoneSound();
         }
 
         if (DoNotDisturbUtils.INSTANCE.shouldVibrate(appPreferences.getShouldVibrateSetting())) {
-            vibrator = (Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
-
-            if (vibrator != null) {
-                long[] vibratePattern = new long[]{0, 400, 800, 600, 800, 800, 800, 1000};
-                int[] amplitudes = new int[]{0, 255, 0, 255, 0, 255, 0, 255};
-
-                VibrationEffect vibrationEffect;
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                    if (vibrator.hasAmplitudeControl()) {
-                        vibrationEffect = VibrationEffect.createWaveform(vibratePattern, amplitudes, -1);
-                        //vibrator.vibrate(vibrationEffect);
-                    } else {
-                        vibrationEffect = VibrationEffect.createWaveform(vibratePattern, -1);
-                        //vibrator.vibrate(vibrationEffect);
-                    }
-                } else {
-                    //vibrator.vibrate(vibratePattern, -1);
-                }
-            }
-
-            handler.postDelayed(() -> {
-                if (vibrator != null) {
-                    vibrator.cancel();
-                }
-            }, 10000);
+            vibrate();
         }
     }
 
-
     @Subscribe(threadMode = ThreadMode.MAIN)
     public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) {
         ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) avatarImageView.getLayoutParams();
@@ -509,4 +496,71 @@ public class CallNotificationController extends BaseController {
             }
         }
     }
+
+    @SuppressLint("LongLogTag")
+    private void playRingtoneSound() {
+        String callRingtonePreferenceString = appPreferences.getCallRingtoneUri();
+        Uri ringtoneUri;
+
+        if (TextUtils.isEmpty(callRingtonePreferenceString)) {
+            // play default sound
+            ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
+                    "/raw/librem_by_feandesign_call");
+        } else {
+            try {
+                RingtoneSettings ringtoneSettings = LoganSquare.parse(callRingtonePreferenceString, RingtoneSettings.class);
+                ringtoneUri = ringtoneSettings.getRingtoneUri();
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to parse ringtone settings");
+                ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() +
+                        "/raw/librem_by_feandesign_call");
+            }
+        }
+
+        if (ringtoneUri != null && getActivity() != null) {
+            mediaPlayer = new MediaPlayer();
+            try {
+                mediaPlayer.setDataSource(getActivity(), ringtoneUri);
+
+                mediaPlayer.setLooping(true);
+                AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes
+                        .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build();
+                mediaPlayer.setAudioAttributes(audioAttributes);
+
+                mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());
+
+                mediaPlayer.prepareAsync();
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to set data source");
+            }
+        }
+    }
+
+    private void vibrate() {
+        vibrator = (Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
+
+        if (vibrator != null) {
+            long[] vibratePattern = new long[]{0, 400, 800, 600, 800, 800, 800, 1000};
+            int[] amplitudes = new int[]{0, 255, 0, 255, 0, 255, 0, 255};
+
+            VibrationEffect vibrationEffect;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                if (vibrator.hasAmplitudeControl()) {
+                    vibrationEffect = VibrationEffect.createWaveform(vibratePattern, amplitudes, -1);
+                    //vibrator.vibrate(vibrationEffect);
+                } else {
+                    vibrationEffect = VibrationEffect.createWaveform(vibratePattern, -1);
+                    //vibrator.vibrate(vibrationEffect);
+                }
+            } else {
+                //vibrator.vibrate(vibratePattern, -1);
+            }
+        }
+
+        handler.postDelayed(() -> {
+            if (vibrator != null) {
+                vibrator.cancel();
+            }
+        }, 10000);
+    }
 }

+ 9 - 16
app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt

@@ -1395,41 +1395,34 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter
 
     private fun startACall(isVoiceOnlyCall: Boolean) {
         isLeavingForConversation = true
-        if (!isVoiceOnlyCall) {
-            val videoCallIntent = getIntentForCall(false)
-            if (videoCallIntent != null) {
-                startActivity(videoCallIntent)
-            }
-        } else {
-            val voiceCallIntent = getIntentForCall(true)
-            if (voiceCallIntent != null) {
-                startActivity(voiceCallIntent)
-            }
+        val callIntent = getIntentForCall(isVoiceOnlyCall)
+        if (callIntent != null) {
+            startActivity(callIntent)
         }
     }
 
     private fun getIntentForCall(isVoiceOnlyCall: Boolean): Intent? {
-        if (currentConversation != null) {
+        currentConversation?.let {
             val bundle = Bundle()
             bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
             bundle.putString(BundleKeys.KEY_ROOM_ID, roomId)
             bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser)
             bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword)
             bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl)
+            bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, it.displayName)
 
             if (isVoiceOnlyCall) {
                 bundle.putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, true)
             }
 
-            if (activity != null) {
+            return if (activity != null) {
                 val callIntent = Intent(activity, MagicCallActivity::class.java)
                 callIntent.putExtras(bundle)
-
-                return callIntent
+                callIntent
             } else {
-                return null
+                null
             }
-        } else {
+        } ?:run {
             return null
         }
     }

+ 2 - 0
app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java

@@ -94,6 +94,8 @@ public class Conversation {
     public Long lobbyTimer;
     @JsonField(name = "lastReadMessage")
     public int lastReadMessage;
+    @JsonField(name = "callFlag")
+    public int callFlag;
 
     public boolean isPublic() {
         return (ConversationType.ROOM_PUBLIC_CALL.equals(type));

+ 4 - 0
app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java

@@ -127,6 +127,10 @@ public class ApiUtils {
         return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + token;
     }
 
+    public static String getRoomV3(String baseUrl, String token) {
+        return baseUrl + ocsApiVersion + "/apps/spreed/api/v3" + "/room/" + token;
+    }
+
     public static RetrofitBucket getRetrofitBucketForCreateRoom(String baseUrl, String roomType,
                                                                 @Nullable String invite,
                                                                 @Nullable String conversationName) {

+ 29 - 0
app/src/main/res/drawable/ic_comment_white.xml

@@ -0,0 +1,29 @@
+<!--
+  ~ Nextcloud Talk application
+  ~  
+  ~ @author Mario Danic
+  ~ Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
+  ~  
+  ~ This program is free software: you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ at your option) any later version.
+  ~  
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~  
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+  <path
+      android:fillColor="#FFFFFF"
+      android:pathData="M6.667,4C4.089,4 2,6.105 2,8.7v11.282c0,2.597 2.09,4.701 4.667,4.701 1.716,0.01 12.083,0.003 17.057,0 1.115,0.842 1.807,1.748 3.057,3.206a0.93,0.93 0,0 0,0.561 0.103,0.969 0.969,0 0,0 0.445,-0.187c0.302,-0.223 0.466,-0.603 0.427,-0.988l-0.314,-2.912a4.699,4.699 0,0 0,2.1 -3.923L30,8.701C30,6.105 27.91,4 25.333,4zM10.4,12.461c1.03,0 1.867,0.842 1.867,1.88 0,1.676 -2.01,2.514 -3.187,1.33 -1.176,-1.184 -0.343,-3.21 1.32,-3.21zM16,12.461c1.03,0 1.867,0.842 1.867,1.88 0,1.676 -2.01,2.514 -3.187,1.33 -1.176,-1.184 -0.343,-3.21 1.32,-3.21zM21.6,12.461c1.03,0 1.867,0.842 1.867,1.88 0,1.676 -2.01,2.514 -3.187,1.33 -1.176,-1.184 -0.343,-3.21 1.32,-3.21z"/>
+</vector>

+ 12 - 11
app/src/main/res/layout/call_item.xml

@@ -26,6 +26,18 @@
     android:layout_weight="1"
     android:orientation="vertical">
 
+    <TextView
+        android:id="@+id/peer_nick_text_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="8dp"
+        android:layout_marginBottom="8dp"
+        android:textColor="@android:color/white"
+        android:layout_centerHorizontal="true"/>
+
     <ImageView
         android:id="@+id/remote_audio_off"
         android:layout_width="16dp"
@@ -47,17 +59,6 @@
         android:src="@drawable/ic_videocam_off_white_24px"
         android:visibility="invisible" />
 
-    <TextView
-        android:id="@+id/peer_nick_text_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentStart="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginStart="8dp"
-        android:layout_marginEnd="8dp"
-        android:layout_marginBottom="8dp"
-        android:textColor="@android:color/white" />
-
     <com.facebook.drawee.view.SimpleDraweeView
         android:id="@+id/avatarImageView"
         android:layout_width="80dp"

+ 6 - 7
app/src/main/res/layout/call_states.xml

@@ -19,25 +19,24 @@
   -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/connectingRelativeLayoutView"
+    android:id="@+id/callStateRelativeLayoutView"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@color/grey950">
+    android:layout_height="wrap_content">
 
     <ImageView
         android:id="@+id/errorImageView"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_above="@id/connectingTextView"
+        android:layout_above="@id/callStateTextView"
         android:layout_centerHorizontal="true"
         android:src="@drawable/ic_signal_wifi_off_white_24dp"
         android:visibility="gone" />
 
     <ProgressBar
-        android:id="@+id/progress_bar"
+        android:id="@+id/callStateProgressBar"
         android:layout_width="@dimen/item_height"
         android:layout_height="@dimen/item_height"
-        android:layout_above="@id/connectingTextView"
+        android:layout_above="@id/callStateTextView"
         android:layout_centerHorizontal="true"
         android:layout_marginStart="@dimen/activity_horizontal_margin"
         android:layout_marginLeft="@dimen/activity_horizontal_margin"
@@ -48,7 +47,7 @@
         android:indeterminateTintMode="src_in" />
 
     <TextView
-        android:id="@+id/connectingTextView"
+        android:id="@+id/callStateTextView"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_centerInParent="true"

+ 157 - 114
app/src/main/res/layout/controller_call.xml

@@ -3,6 +3,8 @@
   ~
   ~ @author Mario Danic
   ~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
+  ~ @author Marcel Hibbe
+  ~ Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
   ~
   ~ This program is free software: you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -19,135 +21,176 @@
   -->
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/relative_layout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@color/grey950"
-    android:fitsSystemWindows="true"
-    tools:context=".activities.MagicCallActivity">
-
-
-    <RelativeLayout
-        android:id="@+id/conversationRelativeLayoutView"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:id="@+id/controllerCallLayout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:visibility="invisible">
+        android:orientation="vertical"
+        tools:context=".activities.MagicCallActivity">
 
+    <LinearLayout
+        android:id="@+id/linearWrapperLayout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-        <LinearLayout
-            android:id="@+id/remote_renderers_layout"
+        <RelativeLayout
+            android:id="@+id/conversationRelativeLayoutView"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:animateLayoutChanges="true"
+            android:layout_height="0dp"
             android:background="@color/grey950"
-            android:orientation="vertical">
-
-        </LinearLayout>
-    </RelativeLayout>
-
-    <RelativeLayout
-        android:id="@+id/callControlsRelativeLayout"
+            android:visibility="visible"
+            android:layout_weight="1"
+            tools:visibility="visible">
+
+            <LinearLayout
+                android:id="@+id/remote_renderers_layout"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:animateLayoutChanges="true"
+                android:orientation="vertical">
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/callInfosLinearLayout"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:animateLayoutChanges="true"
+                android:orientation="vertical"
+                android:gravity="center"
+                android:paddingTop="20dp">
+
+                <TextView
+                    android:id="@+id/callVoiceOrVideoTextView"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:textAlignment="center"
+                    android:textColor="@color/controller_call_incomingCallTextView"
+                    android:textSize="16sp"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent"
+                    tools:text="@string/nc_voice_call"/>
+
+                <TextView
+                    android:id="@+id/callConversationNameTextView"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="10dp"
+                    android:layout_marginBottom="15dp"
+                    android:ellipsize="marquee"
+                    android:textAlignment="center"
+                    android:textColor="@color/white"
+                    android:textSize="28sp"
+                    tools:text="Marsellus Wallace" />
+            </LinearLayout>
+
+            <FrameLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:layout_below="@id/callInfosLinearLayout">
+
+                <org.webrtc.SurfaceViewRenderer
+                    android:id="@+id/pip_video_view"
+                    android:layout_width="@dimen/large_preview_dimension"
+                    android:layout_height="150dp"
+                    android:layout_gravity="center"
+                    android:layout_margin="16dp"
+                    android:visibility="invisible"
+                    tools:visibility="visible"/>
+
+                <com.facebook.drawee.view.SimpleDraweeView xmlns:app="http://schemas.android.com/apk/res-auto"
+                    android:id="@+id/call_control_switch_camera"
+                    android:layout_width="40dp"
+                    android:layout_height="40dp"
+                    android:layout_gravity="center_horizontal|bottom"
+                    android:layout_marginBottom="20dp"
+                    app:placeholderImage="@drawable/ic_switch_video_white_24px"
+                    app:roundAsCircle="true" />
+            </FrameLayout>
+
+            <View android:id="@+id/verticalCenter"
+                android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_centerHorizontal="true"
+                android:layout_centerVertical="true"/>
+
+            <include
+                layout="@layout/call_states"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignTop="@id/verticalCenter"
+                android:layout_marginTop="-50dp"/>
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/callControlsLinearLayout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_marginTop="16dp"
-        android:layout_marginBottom="8dp"
-        android:animateLayoutChanges="true">
-
-        <com.facebook.drawee.view.SimpleDraweeView xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:animateLayoutChanges="true"
+        android:orientation="horizontal"
+        android:background="@android:color/transparent"
+        android:gravity="center"
+        android:layout_alignBottom="@id/linearWrapperLayout"
+        android:layout_marginBottom="10dp">
+
+        <com.facebook.drawee.view.SimpleDraweeView
             android:id="@+id/callControlToggleChat"
             android:layout_width="60dp"
-            android:layout_height="60dp"
-            android:layout_marginStart="16dp"
-            app:backgroundImage="@color/whiteHalfTransparent"
-            app:placeholderImage="@drawable/ic_comment"
+            android:layout_height="80dp"
+            android:layout_marginStart="40dp"
+            android:layout_marginEnd="10dp"
+            app:backgroundImage="@color/call_buttons_background"
+            app:placeholderImage="@drawable/ic_comment_white"
+            app:roundAsCircle="true"
+            android:elevation="10dp"
+            />
+
+        <com.facebook.drawee.view.SimpleDraweeView
+            android:id="@+id/callControlEnableSpeaker"
+            android:layout_width="60dp"
+            android:layout_height="80dp"
+            android:layout_marginStart="10dp"
+            android:layout_marginEnd="10dp"
+            app:backgroundImage="@color/call_buttons_background"
+            app:placeholderImage="@drawable/ic_volume_mute_white_24dp"
             app:roundAsCircle="true" />
 
-        <LinearLayout
-            android:id="@+id/callControlsLinearLayoutView"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            android:layout_centerHorizontal="true"
-            android:layout_marginBottom="24dp"
-            android:animateLayoutChanges="true"
-            android:background="@android:color/transparent"
-            android:gravity="center">
-
-            <com.facebook.drawee.view.SimpleDraweeView xmlns:app="http://schemas.android.com/apk/res-auto"
-                android:id="@+id/call_control_microphone"
-                android:layout_width="60dp"
-                android:layout_height="60dp"
-                android:layout_marginStart="24dp"
-                android:alpha="0.7"
-                app:backgroundImage="@color/colorPrimary"
-                app:placeholderImage="@drawable/ic_mic_off_white_24px"
-                app:roundAsCircle="true" />
-
-            <com.facebook.drawee.view.SimpleDraweeView xmlns:app="http://schemas.android.com/apk/res-auto"
-                android:id="@+id/call_control_camera"
-                android:layout_width="60dp"
-                android:layout_height="60dp"
-                android:layout_marginStart="24dp"
-                android:layout_marginEnd="24dp"
-                android:alpha="0.7"
-                app:backgroundImage="@color/colorPrimary"
-                app:placeholderImage="@drawable/ic_videocam_white_24px"
-                app:roundAsCircle="true" />
-
-            <com.facebook.drawee.view.SimpleDraweeView xmlns:app="http://schemas.android.com/apk/res-auto"
-                android:id="@+id/callControlEnableSpeaker"
-                android:layout_width="60dp"
-                android:layout_height="60dp"
-                android:layout_marginStart="24dp"
-                android:layout_marginEnd="24dp"
-                android:visibility="gone"
-                app:backgroundImage="@color/colorPrimary"
-                app:placeholderImage="@drawable/ic_volume_mute_white_24dp"
-                app:roundAsCircle="true" />
-
-        </LinearLayout>
-
-        <com.facebook.drawee.view.SimpleDraweeView xmlns:app="http://schemas.android.com/apk/res-auto"
-            android:id="@+id/callControlHangupView"
+        <com.facebook.drawee.view.SimpleDraweeView
+            android:id="@+id/call_control_camera"
             android:layout_width="60dp"
-            android:layout_height="60dp"
-            android:layout_above="@id/callControlsLinearLayoutView"
-            android:layout_centerHorizontal="true"
-            app:backgroundImage="@color/nc_darkRed"
-            app:placeholderImage="@drawable/ic_call_end_white_24px"
+            android:layout_height="80dp"
+            android:layout_marginStart="10dp"
+            android:layout_marginEnd="10dp"
+            android:alpha="0.7"
+            app:backgroundImage="@color/call_buttons_background"
+            app:placeholderImage="@drawable/ic_videocam_white_24px"
             app:roundAsCircle="true" />
-    </RelativeLayout>
 
-    <FrameLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentTop="true"
-        android:layout_alignParentEnd="true">
-
-        <org.webrtc.SurfaceViewRenderer
-            android:id="@+id/pip_video_view"
-            android:layout_width="@dimen/large_preview_dimension"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:layout_margin="16dp"
-            android:visibility="invisible" />
-
-        <com.facebook.drawee.view.SimpleDraweeView xmlns:app="http://schemas.android.com/apk/res-auto"
-            android:id="@+id/call_control_switch_camera"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
-            android:layout_gravity="center_horizontal|bottom"
-            android:layout_marginBottom="20dp"
-            app:backgroundImage="@color/colorPrimary"
-            app:placeholderImage="@drawable/ic_switch_video_white_24px"
+        <com.facebook.drawee.view.SimpleDraweeView
+            android:id="@+id/call_control_microphone"
+            android:layout_width="60dp"
+            android:layout_height="80dp"
+            android:layout_marginStart="10dp"
+            android:layout_marginEnd="10dp"
+            android:alpha="0.7"
+            app:backgroundImage="@color/call_buttons_background"
+            app:placeholderImage="@drawable/ic_mic_off_white_24px"
             app:roundAsCircle="true" />
 
-    </FrameLayout>
+        <com.facebook.drawee.view.SimpleDraweeView
+            android:id="@+id/callControlHangupView"
+            android:layout_width="60dp"
+            android:layout_height="80dp"
+            android:layout_marginStart="10dp"
+            android:layout_marginEnd="40dp"
+            app:backgroundImage="@color/nc_darkRed"
+            app:placeholderImage="@drawable/ic_call_end_white_24px"
+            app:roundAsCircle="true" />
+    </LinearLayout>
 
-    <include
-        layout="@layout/call_states"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-</RelativeLayout>
+</RelativeLayout>

+ 17 - 5
app/src/main/res/layout/controller_call_notification.xml

@@ -47,7 +47,8 @@
             android:visibility="gone"
             app:backgroundImage="@color/nc_darkGreen"
             app:placeholderImage="@drawable/ic_call_white_24dp"
-            app:roundAsCircle="true" />
+            app:roundAsCircle="true"
+            tools:visibility="visible"/>
 
         <com.facebook.drawee.view.SimpleDraweeView
             android:id="@+id/callControlHangupView"
@@ -66,7 +67,8 @@
             android:visibility="gone"
             app:backgroundImage="@color/nc_darkGreen"
             app:placeholderImage="@drawable/ic_videocam_white_24px"
-            app:roundAsCircle="true" />
+            app:roundAsCircle="true"
+            tools:visibility="visible"/>
     </LinearLayout>
 
     <RelativeLayout
@@ -75,11 +77,11 @@
         android:layout_height="wrap_content">
 
         <TextView
-            android:id="@+id/incomingCallTextView"
+            android:id="@+id/incomingCallVoiceOrVideoTextView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="16dp"
-            android:text="@string/nc_incoming_call"
+            android:text="@string/nc_unknown_call"
             android:textAlignment="center"
             android:textColor="@color/controller_call_incomingCallTextView"
             android:textSize="16sp"
@@ -91,7 +93,7 @@
             android:id="@+id/conversationNameTextView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_below="@+id/incomingCallTextView"
+            android:layout_below="@+id/incomingCallVoiceOrVideoTextView"
             android:layout_marginBottom="16dp"
             android:ellipsize="marquee"
             android:textAlignment="center"
@@ -99,6 +101,16 @@
             android:textSize="28sp"
             tools:text="Victor Gregorius Magnus" />
 
+        <TextView
+            android:id="@+id/incomingCallDescriptionTextView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/nc_call_incoming"
+            android:textAlignment="center"
+            android:textColor="@color/controller_call_incomingCallTextView"
+            android:textSize="16sp"
+            android:layout_below="@+id/conversationNameTextView" />
+
     </RelativeLayout>
 
     <com.facebook.drawee.view.SimpleDraweeView

+ 9 - 3
app/src/main/res/layout/controller_chat.xml

@@ -20,24 +20,29 @@
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/bbb"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:keepScreenOn="true"
     android:animateLayoutChanges="true">
 
     <include layout="@layout/lobby_view"
-        android:visibility="gone"/>
+        android:visibility="gone"
+        tools:visibility="visible"/>
 
     <com.facebook.drawee.view.SimpleDraweeView
         android:id="@+id/callControlToggleChat"
         android:layout_width="60dp"
         android:layout_height="60dp"
         android:layout_margin="16dp"
-        app:backgroundImage="@color/nc_grey"
+        android:layout_alignParentEnd="true"
+        app:backgroundImage="@color/call_buttons_background"
         app:placeholderImage="@drawable/ic_call_black_24dp"
         app:roundAsCircle="true"
         android:elevation="10dp"
         android:visibility="gone"
+        tools:visibility="visible"
         />
 
     <ProgressBar
@@ -52,7 +57,8 @@
         android:indeterminate="true"
         android:indeterminateTint="@color/colorPrimary"
         android:indeterminateTintMode="src_in"
-        android:visibility="gone" />
+        android:visibility="gone"
+        tools:visibility="visible"/>
 
     <View
         android:id="@+id/separator"

二进制
app/src/main/res/raw/tr110_1_kap8_3_freiton1.ogg


+ 3 - 0
app/src/main/res/values/colors.xml

@@ -64,4 +64,7 @@
     <color name="bg_message_list_outcoming_bubble_deleted">#800082C9</color>
 
     <color name="bg_bottom_sheet">#46ffffff</color>
+
+    <color name="call_buttons_background">#BF999999</color>
+
 </resources>

+ 5 - 0
app/src/main/res/values/strings.xml

@@ -184,6 +184,11 @@
     <string name="nc_permissions_settings">Open settings</string>
 
     <!-- Call -->
+    <string name="nc_voice_call">Nextcloud Talk voice call</string>
+    <string name="nc_video_call">Nextcloud Talk video call</string>
+    <string name="nc_unknown_call">Nextcloud Talk call</string>
+    <string name="nc_call_incoming">INCOMING</string>
+    <string name="nc_call_ringing">RINGING</string>
     <string name="nc_connecting_call">Connecting…</string>
     <string name="nc_calling">Calling…</string>
     <string name="nc_incoming_call">Incoming call from</string>