浏览代码

Fix #554 and fix #557

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 5 年之前
父节点
当前提交
0f0119d22c

+ 240 - 31
app/src/main/java/com/nextcloud/talk/controllers/CallController.java

@@ -26,9 +26,13 @@ import android.animation.AnimatorListenerAdapter;
 import android.annotation.SuppressLint;
 import android.content.res.Configuration;
 import android.graphics.Color;
+import android.media.AudioAttributes;
+import android.media.MediaPlayer;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -143,6 +147,12 @@ public class CallController extends BaseController {
     @BindView(R.id.conversationRelativeLayoutView)
     RelativeLayout conversationView;
 
+    @BindView(R.id.errorImageView)
+    ImageView errorImageView;
+
+    @BindView(R.id.progress_bar)
+    ProgressBar progressBar;
+
     @Inject
     NcApi ncApi;
     @Inject
@@ -210,9 +220,11 @@ public class CallController extends BaseController {
 
     private CallStatus currentCallStatus;
 
+    private MediaPlayer mediaPlayer;
+
     @Parcel
     public enum CallStatus {
-        CALLING, CALLING_TIMEOUT, ESTABLISHED, RECONNECTING, OFFLINE, LEAVING
+        CALLING, CALLING_TIMEOUT, ESTABLISHED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING
     }
 
     public CallController(Bundle args) {
@@ -234,7 +246,7 @@ public class CallController extends BaseController {
         }
 
         powerManagerUtils = new PowerManagerUtils();
-        currentCallStatus = CallStatus.CALLING;
+        setCallState(CallStatus.CALLING);
     }
 
     @Override
@@ -409,6 +421,10 @@ public class CallController extends BaseController {
 
     }
 
+    private boolean isConnectionEstablished() {
+        return (currentCallStatus.equals(CallStatus.ESTABLISHED) || currentCallStatus.equals(CallStatus.IN_CONVERSATION));
+    }
+
     @AfterPermissionGranted(100)
     private void onPermissionsGranted() {
         if (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CALL)) {
@@ -430,7 +446,7 @@ public class CallController extends BaseController {
                 }
             }
 
-            if (!currentCallStatus.equals(CallStatus.ESTABLISHED)) {
+            if (!isConnectionEstablished()) {
                 fetchSignalingSettings();
             }
         } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(),
@@ -469,7 +485,7 @@ public class CallController extends BaseController {
             microphoneControlButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px);
         }
 
-        if (!currentCallStatus.equals(CallStatus.ESTABLISHED)) {
+        if (!isConnectionEstablished()) {
             fetchSignalingSettings();
         }
     }
@@ -487,7 +503,7 @@ public class CallController extends BaseController {
         if (getActivity() != null && (EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA) ||
                 EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_MICROPHONE))) {
             checkIfSomeAreApproved();
-        } else if (!currentCallStatus.equals(CallStatus.ESTABLISHED)) {
+        } else if (!isConnectionEstablished()) {
             fetchSignalingSettings();
         }
     }
@@ -637,7 +653,7 @@ public class CallController extends BaseController {
                 toggleMedia(true, false);
             }
 
-            if (isVoiceOnlyCall && !currentCallStatus.equals(CallStatus.ESTABLISHED)) {
+            if (isVoiceOnlyCall && !isConnectionEstablished()) {
                 fetchSignalingSettings();
             }
 
@@ -659,7 +675,7 @@ public class CallController extends BaseController {
 
     @OnClick(R.id.callControlHangupView)
     void onHangupClick() {
-        currentCallStatus = CallStatus.LEAVING;
+        setCallState(CallStatus.LEAVING);
         hangup(true);
     }
 
@@ -755,7 +771,7 @@ public class CallController extends BaseController {
             }
         }
 
-        if (currentCallStatus.equals(CallStatus.ESTABLISHED)) {
+        if (isConnectionEstablished()) {
             if (!hasMCU) {
                 for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
                     magicPeerConnectionWrapperList.get(i).sendChannelData(new DataChannelMessage(message));
@@ -1058,19 +1074,7 @@ public class CallController extends BaseController {
 
                     @Override
                     public void onNext(GenericOverall genericOverall) {
-                        currentCallStatus = CallStatus.ESTABLISHED;
-
-                        if (connectingView != null) {
-                            connectingView.setVisibility(View.GONE);
-                        }
-
-                        if (conversationView != null) {
-                            conversationView.setVisibility(View.VISIBLE);
-                        }
-
-                        if (!isPTTActive) {
-                            animateCallControls(false, 5000);
-                        }
+                        setCallState(CallStatus.ESTABLISHED);
 
                         ApplicationWideCurrentRoomHolder.getInstance().setInCall(true);
 
@@ -1079,8 +1083,8 @@ public class CallController extends BaseController {
                                     .subscribeOn(Schedulers.io())
                                     .observeOn(AndroidSchedulers.mainThread())
                                     .repeatWhen(observable -> observable.delay(5000, TimeUnit.MILLISECONDS))
-                                    .takeWhile(observable -> currentCallStatus.equals(CallStatus.ESTABLISHED))
-                                    .retry(3, observable -> currentCallStatus.equals(CallStatus.ESTABLISHED))
+                                    .takeWhile(observable -> isConnectionEstablished())
+                                    .retry(3, observable -> isConnectionEstablished())
                                     .subscribe(new Observer<GenericOverall>() {
                                         @Override
                                         public void onSubscribe(Disposable d) {
@@ -1121,8 +1125,8 @@ public class CallController extends BaseController {
                                     .subscribeOn(Schedulers.io())
                                     .observeOn(AndroidSchedulers.mainThread())
                                     .repeatWhen(observable -> observable)
-                                    .takeWhile(observable -> currentCallStatus.equals(CallStatus.ESTABLISHED))
-                                    .retry(3, observable -> currentCallStatus.equals(CallStatus.ESTABLISHED))
+                                    .takeWhile(observable -> isConnectionEstablished())
+                                    .retry(3, observable -> isConnectionEstablished())
                                     .subscribe(new Observer<SignalingOverall>() {
                                         @Override
                                         public void onSubscribe(Disposable d) {
@@ -1246,7 +1250,7 @@ public class CallController extends BaseController {
     private void receivedSignalingMessage(Signaling signaling) throws IOException {
         String messageType = signaling.getType();
 
-        if (!currentCallStatus.equals(CallStatus.ESTABLISHED)) {
+        if (!isConnectionEstablished()) {
             return;
         }
 
@@ -1316,6 +1320,7 @@ public class CallController extends BaseController {
     }
 
     private void hangup(boolean shutDownView) {
+        stopCallingSound();
         dispose(null);
         if (shutDownView) {
 
@@ -1480,7 +1485,7 @@ public class CallController extends BaseController {
         // Calculate sessions that join the call
         newSessions.removeAll(oldSesssions);
 
-        if (!currentCallStatus.equals(CallStatus.ESTABLISHED)) {
+        if (!isConnectionEstablished()) {
             return;
         }
 
@@ -1494,6 +1499,10 @@ public class CallController extends BaseController {
             getPeerConnectionWrapperForSessionIdAndType(sessionId, "video", hasMCU && sessionId.equals(webSocketClient.getSessionId()));
         }
 
+        if (newSessions.size() > 0 && !currentCallStatus.equals(CallStatus.IN_CONVERSATION)) {
+            setCallState(CallStatus.IN_CONVERSATION);
+        }
+
         for (String sessionId : oldSesssions) {
             endPeerConnection(sessionId, false);
         }
@@ -1662,7 +1671,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) || currentCallStatus.equals(CallStatus.ESTABLISHED)) && videoOn
+                        (currentCallStatus.equals(CallStatus.CALLING) || isConnectionEstablished()) && videoOn
                         && enableVideo != localVideoTrack.enabled()) {
                     toggleMedia(enableVideo, true);
                 }
@@ -1693,7 +1702,7 @@ public class CallController extends BaseController {
                 int finalI = i;
                 Observable
                         .interval(1, TimeUnit.SECONDS)
-                        .takeWhile(observer -> currentCallStatus.equals(CallStatus.ESTABLISHED))
+                        .takeWhile(observer -> isConnectionEstablished())
                         .observeOn(Schedulers.io())
                         .doOnNext(n -> magicPeerConnectionWrapperList.get(finalI).sendChannelData(dataChannelMessage));
                 break;
@@ -1946,6 +1955,206 @@ public class CallController extends BaseController {
         }
     }
 
+    @OnClick(R.id.connectingRelativeLayoutView)
+    public void onConnectingViewClick() {
+        if (currentCallStatus.equals(CallStatus.CALLING_TIMEOUT)) {
+            setCallState(CallStatus.RECONNECTING);
+            hangupNetworkCalls(false);
+        }
+    }
+
+    private void setCallState(CallStatus callState) {
+        if (currentCallStatus == null || !currentCallStatus.equals(callState)) {
+            currentCallStatus = callState;
+            if (handler == null) {
+                handler = new Handler(Looper.getMainLooper());
+            } else {
+                handler.removeCallbacksAndMessages(null);
+            }
+
+            switch (callState) {
+                case CALLING:
+                    handler.post(() -> {
+                        playCallingSound();
+                        connectingTextView.setText(R.string.nc_connecting_call);
+                        if (connectingView.getVisibility() != View.VISIBLE) {
+                            connectingView.setVisibility(View.VISIBLE);
+                        }
+
+                        if (conversationView.getVisibility() != View.INVISIBLE) {
+                            conversationView.setVisibility(View.INVISIBLE);
+                        }
+
+                        if (progressBar.getVisibility() != View.VISIBLE) {
+                            progressBar.setVisibility(View.VISIBLE);
+                        }
+
+                        if (errorImageView.getVisibility() != View.GONE) {
+                            errorImageView.setVisibility(View.GONE);
+                        }
+                    });
+                    break;
+                case CALLING_TIMEOUT:
+                    handler.post(() -> {
+                        hangup(false);
+                        connectingTextView.setText(R.string.nc_call_timeout);
+                        if (connectingView.getVisibility() != View.VISIBLE) {
+                            connectingView.setVisibility(View.VISIBLE);
+                        }
+
+                        if (progressBar.getVisibility() != View.GONE) {
+                            progressBar.setVisibility(View.GONE);
+                        }
+
+                        if (conversationView.getVisibility() != View.INVISIBLE) {
+                            conversationView.setVisibility(View.INVISIBLE);
+                        }
+
+                        errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp);
+
+                        if (errorImageView.getVisibility() != View.VISIBLE) {
+                            errorImageView.setVisibility(View.VISIBLE);
+                        }
+                    });
+                    break;
+                case RECONNECTING:
+                    handler.post(() -> {
+                        playCallingSound();
+                        connectingTextView.setText(R.string.nc_call_reconnecting);
+                        if (connectingView.getVisibility() != View.VISIBLE) {
+                            connectingView.setVisibility(View.VISIBLE);
+                        }
+                        if (conversationView.getVisibility() != View.INVISIBLE) {
+                            conversationView.setVisibility(View.INVISIBLE);
+                        }
+                        if (progressBar.getVisibility() != View.VISIBLE) {
+                            progressBar.setVisibility(View.VISIBLE);
+                        }
+
+                        if (errorImageView.getVisibility() != View.GONE) {
+                            errorImageView.setVisibility(View.GONE);
+                        }
+                    });
+                    break;
+                case ESTABLISHED:
+                    handler.postDelayed(() -> setCallState(CallStatus.CALLING_TIMEOUT), 45000);
+                    handler.post(() -> {
+                        connectingTextView.setText(R.string.nc_calling);
+                        if (connectingTextView.getVisibility() != View.VISIBLE) {
+                            connectingView.setVisibility(View.VISIBLE);
+                        }
+
+                        if (progressBar.getVisibility() != View.VISIBLE) {
+                            progressBar.setVisibility(View.VISIBLE);
+                        }
+
+                        if (conversationView.getVisibility() != View.INVISIBLE) {
+                            conversationView.setVisibility(View.INVISIBLE);
+                        }
+
+                        if (errorImageView.getVisibility() != View.GONE) {
+                            errorImageView.setVisibility(View.GONE);
+                        }
+                    });
+                    break;
+                case IN_CONVERSATION:
+                    handler.post(() -> {
+                        stopCallingSound();
+
+                        if (!isPTTActive) {
+                            animateCallControls(false, 5000);
+                        }
+
+                        if (connectingView.getVisibility() != View.INVISIBLE) {
+                            connectingView.setVisibility(View.INVISIBLE);
+                        }
+
+                        if (progressBar.getVisibility() != View.GONE) {
+                            progressBar.setVisibility(View.GONE);
+                        }
+
+                        if (conversationView.getVisibility() != View.VISIBLE) {
+                            conversationView.setVisibility(View.VISIBLE);
+                        }
+
+                        if (errorImageView.getVisibility() != View.GONE) {
+                            errorImageView.setVisibility(View.GONE);
+                        }
+                    });
+                    break;
+                case OFFLINE:
+                    handler.post(() -> {
+                        stopCallingSound();
+                        connectingTextView.setText(R.string.nc_offline);
+
+                        if (connectingView.getVisibility() != View.VISIBLE) {
+                            connectingView.setVisibility(View.VISIBLE);
+                        }
+
+                        if (conversationView.getVisibility() != View.INVISIBLE) {
+                            conversationView.setVisibility(View.INVISIBLE);
+                        }
+
+                        if (progressBar.getVisibility() != View.GONE) {
+                            progressBar.setVisibility(View.GONE);
+                        }
+
+                        errorImageView.setImageResource(R.drawable.ic_signal_wifi_off_white_24dp);
+                        if (errorImageView.getVisibility() != View.VISIBLE) {
+                            errorImageView.setVisibility(View.VISIBLE);
+                        }
+                    });
+                    break;
+                case LEAVING:
+                    handler.post(() -> {
+                        if (!isDestroyed() && !isBeingDestroyed()) {
+                            stopCallingSound();
+                            connectingTextView.setText(R.string.nc_leaving_call);
+                            connectingView.setVisibility(View.VISIBLE);
+                            conversationView.setVisibility(View.INVISIBLE);
+                            progressBar.setVisibility(View.VISIBLE);
+                            errorImageView.setVisibility(View.GONE);
+                        }
+                    });
+                    break;
+                default:
+            }
+        }
+    }
+
+    private void playCallingSound() {
+        stopCallingSound();
+        Uri ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/raw/librem_by_feandesign_call");
+        if (getActivity() != null) {
+            mediaPlayer = new MediaPlayer();
+            try {
+                mediaPlayer.setDataSource(Objects.requireNonNull(getActivity()), ringtoneUri);
+                mediaPlayer.setLooping(true);
+                AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes
+                        .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build();
+                mediaPlayer.setAudioAttributes(audioAttributes);
+
+                mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());
+
+                mediaPlayer.prepareAsync();
+
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to play sound");
+            }
+        }
+    }
+
+    private void stopCallingSound() {
+        if (mediaPlayer != null) {
+            if (mediaPlayer.isPlaying()) {
+                mediaPlayer.stop();
+            }
+
+            mediaPlayer.release();
+            mediaPlayer = null;
+        }
+    }
+
     @Override
     protected void onAttach(@NonNull View view) {
         super.onAttach(view);
@@ -1990,7 +2199,7 @@ public class CallController extends BaseController {
                 handler.removeCallbacksAndMessages(null);
             }
 
-            currentCallStatus = CallStatus.RECONNECTING;
+            setCallState(CallStatus.RECONNECTING);
             hangupNetworkCalls(false);
 
         } else if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) {
@@ -1998,7 +2207,7 @@ public class CallController extends BaseController {
                 handler.removeCallbacksAndMessages(null);
             }
 
-            currentCallStatus = CallStatus.OFFLINE;
+            setCallState(CallStatus.OFFLINE);
             hangup(false);
         }
     }

+ 25 - 0
app/src/main/res/drawable/ic_av_timer_timer_24dp.xml

@@ -0,0 +1,25 @@
+<!--
+  ~ 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 android:autoMirrored="true" android:height="24dp"
+    android:tint="#FFFFFF" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M11,17c0,0.55 0.45,1 1,1s1,-0.45 1,-1 -0.45,-1 -1,-1 -1,0.45 -1,1zM11,3v4h2L13,5.08c3.39,0.49 6,3.39 6,6.92 0,3.87 -3.13,7 -7,7s-7,-3.13 -7,-7c0,-1.68 0.59,-3.22 1.58,-4.42L12,13l1.41,-1.41 -6.8,-6.8v0.02C4.42,6.45 3,9.05 3,12c0,4.97 4.02,9 9,9 4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9h-1zM18,12c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1 0.45,1 1,1 1,-0.45 1,-1zM6,12c0,0.55 0.45,1 1,1s1,-0.45 1,-1 -0.45,-1 -1,-1 -1,0.45 -1,1z"/>
+</vector>

+ 25 - 0
app/src/main/res/drawable/ic_signal_wifi_off_white_24dp.xml

@@ -0,0 +1,25 @@
+<!--
+  ~ 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 android:autoMirrored="true" android:height="24dp"
+    android:tint="#FFFFFF" android:viewportHeight="24.0"
+    android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4 -1.5,0 -2.89,0.19 -4.15,0.48L18.18,13.8 23.64,7zM17.04,15.22L3.27,1.44 2,2.72l2.05,2.06C1.91,5.76 0.59,6.82 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01 3.9,-4.86 3.32,3.32 1.27,-1.27 -3.46,-3.46z"/>
+</vector>

+ 61 - 0
app/src/main/res/layout/call_states.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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/>.
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/connectingRelativeLayoutView"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/grey950">
+
+    <ProgressBar
+        android:id="@+id/progress_bar"
+        android:layout_width="@dimen/item_height"
+        android:layout_height="@dimen/item_height"
+        android:layout_above="@id/connectingTextView"
+        android:layout_centerHorizontal="true"
+        android:layout_marginStart="@dimen/activity_horizontal_margin"
+        android:layout_marginLeft="@dimen/activity_horizontal_margin"
+        android:layout_marginEnd="@dimen/activity_horizontal_margin"
+        android:layout_marginRight="@dimen/activity_horizontal_margin"
+        android:indeterminate="true"
+        android:indeterminateTint="@color/colorPrimary"
+        android:indeterminateTintMode="src_in" />
+
+    <ImageView
+        android:id="@+id/errorImageView"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_above="@id/connectingTextView"
+        android:layout_centerHorizontal="true"
+        android:src="@drawable/ic_signal_wifi_off_white_24dp"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@+id/connectingTextView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true"
+        android:layout_margin="16dp"
+        android:gravity="center"
+        android:text="@string/nc_connecting_call"
+        android:textAlignment="center"
+        android:textColor="@color/white" />
+
+</RelativeLayout>

+ 3 - 29
app/src/main/res/layout/controller_call.xml

@@ -27,36 +27,10 @@
     android:fitsSystemWindows="true"
     tools:context=".activities.MagicCallActivity">
 
-    <RelativeLayout
-        android:id="@+id/connectingRelativeLayoutView"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <ProgressBar
-            android:id="@+id/progress_bar"
-            android:layout_width="@dimen/item_height"
-            android:layout_height="@dimen/item_height"
-            android:layout_centerInParent="true"
-            android:layout_marginStart="@dimen/activity_horizontal_margin"
-            android:layout_marginLeft="@dimen/activity_horizontal_margin"
-            android:layout_marginEnd="@dimen/activity_horizontal_margin"
-            android:layout_marginRight="@dimen/activity_horizontal_margin"
-            android:indeterminate="true"
-            android:indeterminateTint="@color/colorPrimary"
-            android:indeterminateTintMode="src_in" />
-
-        <TextView
-            android:id="@+id/connectingTextView"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/progress_bar"
-            android:layout_centerHorizontal="true"
-            android:layout_margin="16dp"
-            android:text="@string/nc_connecting_call"
-            android:textAlignment="center"
-            android:textColor="@color/white" />
 
-    </RelativeLayout>
+    <include layout="@layout/call_states"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
 
     <RelativeLayout
         android:id="@+id/conversationRelativeLayoutView"

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

@@ -168,11 +168,16 @@
 
     <!-- Call -->
     <string name="nc_connecting_call">Connecting…</string>
+    <string name="nc_calling">Calling…</string>
     <string name="nc_incoming_call">Incoming call from</string>
     <string name="nc_nick_guest">Guest</string>
     <string name="nc_public_call">New public conversation</string>
     <string name="nc_public_call_explanation">Public conversations let you invite people from outside through a
         specially crafted link.</string>
+    <string name="nc_call_timeout">No response in 45 seconds, tap to try again</string>
+    <string name="nc_call_reconnecting">Reconnecting…</string>
+    <string name="nc_offline">Currently offline, please check your connectivity</string>
+    <string name="nc_leaving_call">Leaving call…</string>
 
     <!-- Notification channels -->
     <string name="nc_notification_channel">%1$s on %2$s notification channel</string>
@@ -266,4 +271,5 @@
     <string name="nc_file_browser_back">Back</string>
     <string name="nc_file_browser_refresh">Refresh</string>
     <string name="nc_last_modified">%1$s | Last modified: %2$s</string>
+
 </resources>