Add call recording functionality in call view

This commit is contained in:
Mickaël Turnel 2018-11-20 16:55:56 +01:00
parent a895fde7eb
commit bcb5e2da66
10 changed files with 170 additions and 55 deletions

View file

@ -637,6 +637,10 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
changeCurrentFragment(FragmentsAvailable.ABOUT, null); changeCurrentFragment(FragmentsAvailable.ABOUT, null);
} }
public void displayRecordings() {
}
public void displayContactsForEdition(String sipAddress, String displayName) { public void displayContactsForEdition(String sipAddress, String displayName) {
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putBoolean("EditOnClick", true); extras.putBoolean("EditOnClick", true);
@ -1606,6 +1610,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
if (getResources().getBoolean(R.bool.enable_in_app_purchase)) { if (getResources().getBoolean(R.bool.enable_in_app_purchase)) {
sideMenuItems.add(new MenuItem(getResources().getString(R.string.inapp), R.drawable.menu_options)); sideMenuItems.add(new MenuItem(getResources().getString(R.string.inapp), R.drawable.menu_options));
} }
sideMenuItems.add(new MenuItem(getResources().getString(R.string.menu_recordings), R.drawable.menu_recordings));
sideMenuItems.add(new MenuItem(getResources().getString(R.string.menu_about), R.drawable.menu_about)); sideMenuItems.add(new MenuItem(getResources().getString(R.string.menu_about), R.drawable.menu_about));
sideMenuContent = findViewById(R.id.side_menu_content); sideMenuContent = findViewById(R.id.side_menu_content);
sideMenuItemList = findViewById(R.id.item_list); sideMenuItemList = findViewById(R.id.item_list);
@ -1629,6 +1634,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
LinphoneActivity.instance().displayInapp(); LinphoneActivity.instance().displayInapp();
} }
} }
if (sideMenuItemList.getAdapter().getItem(i).toString().equals(R.string.menu_recordings)) {
LinphoneActivity.instance().displayRecordings();
}
openOrCloseSideMenu(false); openOrCloseSideMenu(false);
} }
}); });

View file

@ -101,6 +101,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
private static final int PERMISSIONS_REQUEST_CAMERA = 202; private static final int PERMISSIONS_REQUEST_CAMERA = 202;
private static final int PERMISSIONS_ENABLED_CAMERA = 203; private static final int PERMISSIONS_ENABLED_CAMERA = 203;
private static final int PERMISSIONS_ENABLED_MIC = 204; private static final int PERMISSIONS_ENABLED_MIC = 204;
private static final int PERMISSIONS_EXTERNAL_STORAGE = 205;
private static CallActivity instance; private static CallActivity instance;
@ -109,14 +110,14 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
private ImageView switchCamera; private ImageView switchCamera;
private TextView missedChats; private TextView missedChats;
private RelativeLayout mActiveCallHeader, sideMenuContent, avatar_layout; private RelativeLayout mActiveCallHeader, sideMenuContent, avatar_layout;
private ImageView pause, hangUp, dialer, video, micro, speaker, options, addCall, transfer, conference, conferenceStatus, contactPicture; private ImageView pause, hangUp, dialer, video, micro, speaker, options, addCall, transfer, conference, conferenceStatus, contactPicture, recordCall, recording;
private ImageView audioRoute, routeSpeaker, routeEarpiece, routeBluetooth, menu, chat; private ImageView audioRoute, routeSpeaker, routeEarpiece, routeBluetooth, menu, chat;
private LinearLayout mNoCurrentCall, callInfo, mCallPaused; private LinearLayout mNoCurrentCall, callInfo, mCallPaused;
private ProgressBar videoProgress; private ProgressBar videoProgress;
private StatusFragment status; private StatusFragment status;
private CallAudioFragment audioCallFragment; private CallAudioFragment audioCallFragment;
private CallVideoFragment videoCallFragment; private CallVideoFragment videoCallFragment;
private boolean isSpeakerEnabled = false, isMicMuted = false, isTransferAllowed, isVideoAsk; private boolean isSpeakerEnabled = false, isMicMuted = false, isTransferAllowed, isVideoAsk, isRecording = false;
private LinearLayout mControlsLayout; private LinearLayout mControlsLayout;
private Numpad numpad; private Numpad numpad;
private int cameraNumber; private int cameraNumber;
@ -194,7 +195,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
return; return;
} else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) { } else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) {
if (LinphoneManager.getLc().getCurrentCall() != null) { if (LinphoneManager.getLc().getCurrentCall() != null) {
enabledVideoButton(false); video.setEnabled(false);
} }
if (isVideoEnabled(call)) { if (isVideoEnabled(call)) {
showAudioView(); showAudioView();
@ -207,7 +208,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
} }
} }
if (LinphoneManager.getLc().getCurrentCall() != null) { if (LinphoneManager.getLc().getCurrentCall() != null) {
enabledVideoButton(true); video.setEnabled(true);
} }
} else if (state == State.StreamsRunning) { } else if (state == State.StreamsRunning) {
switchVideo(isVideoEnabled(call)); switchVideo(isVideoEnabled(call));
@ -344,7 +345,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
//TopBar //TopBar
video = findViewById(R.id.video); video = findViewById(R.id.video);
video.setOnClickListener(this); video.setOnClickListener(this);
enabledVideoButton(false); video.setEnabled(false);
videoProgress = findViewById(R.id.video_in_progress); videoProgress = findViewById(R.id.video_in_progress);
videoProgress.setVisibility(View.GONE); videoProgress.setVisibility(View.GONE);
@ -380,7 +381,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
pause = findViewById(R.id.pause); pause = findViewById(R.id.pause);
pause.setOnClickListener(this); pause.setOnClickListener(this);
enabledPauseButton(false); pause.setEnabled(false);
mActiveCallHeader = findViewById(R.id.active_call); mActiveCallHeader = findViewById(R.id.active_call);
mNoCurrentCall = findViewById(R.id.no_current_call); mNoCurrentCall = findViewById(R.id.no_current_call);
@ -402,6 +403,15 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
conference.setEnabled(false); conference.setEnabled(false);
conference.setOnClickListener(this); conference.setOnClickListener(this);
recordCall = findViewById(R.id.record_call);
recordCall.setOnClickListener(this);
recordCall.setEnabled(false);
recording = findViewById(R.id.recording);
recording.setOnClickListener(this);
recording.setEnabled(false);
recording.setVisibility(View.GONE);
try { try {
audioRoute = findViewById(R.id.audio_route); audioRoute = findViewById(R.id.audio_route);
audioRoute.setOnClickListener(this); audioRoute.setOnClickListener(this);
@ -489,6 +499,15 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
} }
}); });
break; break;
case PERMISSIONS_EXTERNAL_STORAGE:
LinphoneUtils.dispatchOnUIThread(new Runnable() {
@Override
public void run() {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
toggleCallRecording(!isRecording);
}
}
});
} }
} }
@ -528,7 +547,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
public void refreshInCallActions() { public void refreshInCallActions() {
if (!LinphonePreferences.instance().isVideoEnabled() || isConferenceRunning) { if (!LinphonePreferences.instance().isVideoEnabled() || isConferenceRunning) {
enabledVideoButton(false); video.setEnabled(false);
} else { } else {
if (video.isEnabled()) { if (video.isEnabled()) {
if (isVideoEnabled(LinphoneManager.getLc().getCurrentCall())) { if (isVideoEnabled(LinphoneManager.getLc().getCurrentCall())) {
@ -582,33 +601,27 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
} }
//Enabled transfer button //Enabled transfer button
if (isTransferAllowed && !LinphoneManager.getLc().soundResourcesLocked()) transfer.setEnabled(isTransferAllowed && !LinphoneManager.getLc().soundResourcesLocked());
enabledTransferButton(true);
//Enable conference button //Enable conference button
if (LinphoneManager.getLc().getCallsNb() > 1 && LinphoneManager.getLc().getCallsNb() > confsize && !LinphoneManager.getLc().soundResourcesLocked()) { conference.setEnabled(LinphoneManager.getLc().getCallsNb() > 1 && LinphoneManager.getLc().getCallsNb() > confsize && !LinphoneManager.getLc().soundResourcesLocked());
enabledConferenceButton(true);
} else {
enabledConferenceButton(false);
}
addCall.setEnabled(LinphoneManager.getLc().getCallsNb() < LinphoneManager.getLc().getMaxCalls() && !LinphoneManager.getLc().soundResourcesLocked()); addCall.setEnabled(LinphoneManager.getLc().getCallsNb() < LinphoneManager.getLc().getMaxCalls() && !LinphoneManager.getLc().soundResourcesLocked());
options.setEnabled(!getResources().getBoolean(R.bool.disable_options_in_call) && (addCall.isEnabled() || transfer.isEnabled())); options.setEnabled(!getResources().getBoolean(R.bool.disable_options_in_call) && (addCall.isEnabled() || transfer.isEnabled()));
if (LinphoneManager.getLc().getCurrentCall() != null && LinphonePreferences.instance().isVideoEnabled() && !LinphoneManager.getLc().getCurrentCall().mediaInProgress()) { recordCall.setEnabled(!LinphoneManager.getLc().soundResourcesLocked());
enabledVideoButton(true); recordCall.setImageResource(isRecording ? R.drawable.options_rec_selected : R.drawable.options_rec_default);
} else {
enabledVideoButton(false); recording.setEnabled(isRecording);
} recording.setVisibility(isRecording ? View.VISIBLE : View.GONE);
if (LinphoneManager.getLc().getCurrentCall() != null && !LinphoneManager.getLc().getCurrentCall().mediaInProgress()) {
enabledPauseButton(true);
} else { video.setEnabled(LinphoneManager.getLc().getCurrentCall() != null && LinphonePreferences.instance().isVideoEnabled() && !LinphoneManager.getLc().getCurrentCall().mediaInProgress());
enabledPauseButton(false);
} pause.setEnabled(LinphoneManager.getLc().getCurrentCall() != null && !LinphoneManager.getLc().getCurrentCall().mediaInProgress());
micro.setEnabled(true); micro.setEnabled(true);
if (!isTablet()) { speaker.setEnabled(!isTablet());
speaker.setEnabled(true);
}
transfer.setEnabled(true); transfer.setEnabled(true);
pause.setEnabled(true); pause.setEnabled(true);
dialer.setEnabled(true); dialer.setEnabled(true);
@ -649,6 +662,17 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
toggleSpeaker(); toggleSpeaker();
} else if (id == R.id.add_call) { } else if (id == R.id.add_call) {
goBackToDialer(); goBackToDialer();
} else if (id == R.id.record_call) {
int externalStorage = getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, getPackageName());
Log.i("[Permission] External storage permission is " + (externalStorage == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
if (externalStorage == PackageManager.PERMISSION_GRANTED) {
toggleCallRecording(!isRecording);
} else {
checkAndRequestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, PERMISSIONS_EXTERNAL_STORAGE);
}
} else if (id == R.id.recording) {
toggleCallRecording(false);
} else if (id == R.id.pause) { } else if (id == R.id.pause) {
pauseOrResumeCall(LinphoneManager.getLc().getCurrentCall()); pauseOrResumeCall(LinphoneManager.getLc().getCurrentCall());
} else if (id == R.id.hang_up) { } else if (id == R.id.hang_up) {
@ -700,35 +724,31 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
} }
} }
private void enabledVideoButton(boolean enabled) { private void toggleCallRecording(boolean enable) {
if (enabled) { Call call = LinphoneManager.getLc().getCurrentCall();
video.setEnabled(true);
} else {
video.setEnabled(false);
}
}
private void enabledPauseButton(boolean enabled) { if (call == null) return;
if (enabled) {
pause.setEnabled(true);
} else {
pause.setEnabled(false);
}
}
private void enabledTransferButton(boolean enabled) { if (enable && !isRecording) {
if (enabled) { call.startRecording();
transfer.setEnabled(true); Log.d("start call recording");
} else {
transfer.setEnabled(false);
}
}
private void enabledConferenceButton(boolean enabled) { recordCall.setImageResource(R.drawable.options_rec_selected);
if (enabled) {
conference.setEnabled(true); recording.setVisibility(View.VISIBLE);
} else { recording.setEnabled(true);
conference.setEnabled(false);
isRecording = true;
} else if (!enable && isRecording) {
call.stopRecording();
Log.d("stop call recording");
recordCall.setImageResource(R.drawable.options_rec_default);
recording.setVisibility(View.GONE);
recording.setEnabled(false);
isRecording = false;
} }
} }
@ -910,6 +930,10 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
Core lc = LinphoneManager.getLc(); Core lc = LinphoneManager.getLc();
Call currentCall = lc.getCurrentCall(); Call currentCall = lc.getCurrentCall();
if (isRecording) {
toggleCallRecording(false);
}
if (currentCall != null) { if (currentCall != null) {
lc.terminateCall(currentCall); lc.terminateCall(currentCall);
} else if (lc.isInConference()) { } else if (lc.isInConference()) {
@ -963,6 +987,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
transfer.setVisibility(View.INVISIBLE); transfer.setVisibility(View.INVISIBLE);
addCall.setVisibility(View.INVISIBLE); addCall.setVisibility(View.INVISIBLE);
conference.setVisibility(View.INVISIBLE); conference.setVisibility(View.INVISIBLE);
recordCall.setVisibility(View.INVISIBLE);
displayVideoCall(false); displayVideoCall(false);
numpad.setVisibility(View.GONE); numpad.setVisibility(View.GONE);
options.setSelected(false); options.setSelected(false);
@ -1023,12 +1048,14 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
} }
addCall.setVisibility(View.INVISIBLE); addCall.setVisibility(View.INVISIBLE);
conference.setVisibility(View.INVISIBLE); conference.setVisibility(View.INVISIBLE);
recordCall.setVisibility(View.INVISIBLE);
} else { //Display options } else { //Display options
if (isTransferAllowed) { if (isTransferAllowed) {
transfer.setVisibility(View.VISIBLE); transfer.setVisibility(View.VISIBLE);
} }
addCall.setVisibility(View.VISIBLE); addCall.setVisibility(View.VISIBLE);
conference.setVisibility(View.VISIBLE); conference.setVisibility(View.VISIBLE);
recordCall.setVisibility(View.VISIBLE);
options.setSelected(true); options.setSelected(true);
transfer.setEnabled(LinphoneManager.getLc().getCurrentCall() != null); transfer.setEnabled(LinphoneManager.getLc().getCurrentCall() != null);
} }

View file

@ -19,6 +19,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
@ -28,6 +29,7 @@ import android.provider.OpenableColumns;
import android.text.TextUtils; import android.text.TextUtils;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.core.Address;
import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessage;
import org.linphone.core.Content; import org.linphone.core.Content;
import org.linphone.core.Friend; import org.linphone.core.Friend;
@ -40,6 +42,7 @@ import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
@ -197,6 +200,30 @@ public class FileUtils {
return storageDir; return storageDir;
} }
public static String getRecordingsDirectory(Context mContext) {
String recordingsDir = Environment.getExternalStorageDirectory() + "/" + mContext.getString(mContext.getResources().getIdentifier("app_name", "string", mContext.getPackageName())) + "/recordings";
File file = new File(recordingsDir);
if (!file.isDirectory() || !file.exists()) {
Log.w("Directory " + file + " doesn't seem to exists yet, let's create it");
file.mkdirs();
LinphoneManager.getInstance().getMediaScanner().scanFile(file);
}
return recordingsDir;
}
@SuppressLint("SimpleDateFormat")
public static String getCallRecordingFilename(Context context, Address address) {
String fileName = getRecordingsDirectory(context) + "/";
String name = address.getDisplayName() == null ? address.getUsername() : address.getDisplayName();
fileName += name + "_";
DateFormat format = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss");
fileName += format.format(new Date()) + ".mkv";
return fileName;
}
public static void scanFile(ChatMessage message) { public static void scanFile(ChatMessage message) {
String appData = message.getAppdata(); String appData = message.getAppdata();
if (appData == null) { if (appData == null) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -181,6 +181,19 @@
android:layout_gravity="center"/> android:layout_gravity="center"/>
</LinearLayout> </LinearLayout>
<ImageView
android:id="@+id/recording"
android:src="@drawable/recording"
android:background="@drawable/round_orange_button_background"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="8dp"
android:layout_margin="20dp"
android:contentDescription="@string/content_description_record_call"
android:visibility="gone"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true" />
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout
@ -369,10 +382,21 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" /> android:layout_height="60dp" />
<ImageView
android:id="@+id/record_call"
android:src="@drawable/options_rec_default"
android:background="@drawable/button_background"
android:contentDescription="@string/content_description_record_call"
android:visibility="gone"
android:padding="15dp"
android:layout_above="@id/options"
android:layout_width="match_parent"
android:layout_height="60dp" />
<ImageView <ImageView
android:id="@+id/add_call" android:id="@+id/add_call"
android:visibility="gone" android:visibility="gone"
android:layout_above="@id/options" android:layout_above="@id/record_call"
android:src="@drawable/options_add_call" android:src="@drawable/options_add_call"
android:background="@drawable/button_background" android:background="@drawable/button_background"
android:contentDescription="@string/content_description_add_call" android:contentDescription="@string/content_description_add_call"

View file

@ -181,6 +181,19 @@
android:layout_gravity="center"/> android:layout_gravity="center"/>
</LinearLayout> </LinearLayout>
<ImageView
android:id="@+id/recording"
android:src="@drawable/recording"
android:background="@drawable/round_orange_button_background"
android:layout_width="50dp"
android:layout_height="50dp"
android:padding="10dp"
android:layout_margin="20dp"
android:contentDescription="@string/content_description_record_call"
android:visibility="gone"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true" />
</RelativeLayout> </RelativeLayout>
<LinearLayout <LinearLayout
@ -310,10 +323,21 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" /> android:layout_height="60dp" />
<ImageView
android:id="@+id/record_call"
android:src="@drawable/options_rec_default"
android:background="@drawable/button_background"
android:contentDescription="@string/content_description_record_call"
android:visibility="gone"
android:padding="15dp"
android:layout_above="@id/options"
android:layout_width="match_parent"
android:layout_height="60dp" />
<ImageView <ImageView
android:id="@+id/add_call" android:id="@+id/add_call"
android:visibility="gone" android:visibility="gone"
android:layout_above="@id/options" android:layout_above="@id/record_call"
android:src="@drawable/options_add_call" android:src="@drawable/options_add_call"
android:background="@drawable/button_background" android:background="@drawable/button_background"
android:contentDescription="@string/content_description_add_call" android:contentDescription="@string/content_description_add_call"

View file

@ -250,6 +250,7 @@
<!-- Side Menu --> <!-- Side Menu -->
<string name="menu_assistant">Assistant</string> <string name="menu_assistant">Assistant</string>
<string name="menu_settings">Settings</string> <string name="menu_settings">Settings</string>
<string name="menu_recordings">My recordings</string>
<string name="menu_about">About</string> <string name="menu_about">About</string>
<string name="quit">Quit</string> <string name="quit">Quit</string>
@ -286,6 +287,9 @@
<string name="call_stats_display_filter">Display filter:</string> <string name="call_stats_display_filter">Display filter:</string>
<string name="call">Call</string> <string name="call">Call</string>
<!-- Recordings -->
<string name="no_recordings">No recordings.</string>
<!-- About --> <!-- About -->
<string name="menu_send_log">Send log</string> <string name="menu_send_log">Send log</string>
<string name="menu_reset_log">Reset log</string> <string name="menu_reset_log">Reset log</string>
@ -546,4 +550,5 @@
<string name="content_title_notification">Linphone instant messages notifications</string> <string name="content_title_notification">Linphone instant messages notifications</string>
<string name="content_description_conversation_subject">Group chat room subject</string> <string name="content_description_conversation_subject">Group chat room subject</string>
<string name="content_description_conversation_infos">Group chat room info</string> <string name="content_description_conversation_infos">Group chat room info</string>
<string name="content_description_record_call">Record call</string>
</resources> </resources>