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);
}
public void displayRecordings() {
}
public void displayContactsForEdition(String sipAddress, String displayName) {
Bundle extras = new Bundle();
extras.putBoolean("EditOnClick", true);
@ -1606,6 +1610,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
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.menu_recordings), R.drawable.menu_recordings));
sideMenuItems.add(new MenuItem(getResources().getString(R.string.menu_about), R.drawable.menu_about));
sideMenuContent = findViewById(R.id.side_menu_content);
sideMenuItemList = findViewById(R.id.item_list);
@ -1629,6 +1634,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
LinphoneActivity.instance().displayInapp();
}
}
if (sideMenuItemList.getAdapter().getItem(i).toString().equals(R.string.menu_recordings)) {
LinphoneActivity.instance().displayRecordings();
}
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_ENABLED_CAMERA = 203;
private static final int PERMISSIONS_ENABLED_MIC = 204;
private static final int PERMISSIONS_EXTERNAL_STORAGE = 205;
private static CallActivity instance;
@ -109,14 +110,14 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
private ImageView switchCamera;
private TextView missedChats;
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 LinearLayout mNoCurrentCall, callInfo, mCallPaused;
private ProgressBar videoProgress;
private StatusFragment status;
private CallAudioFragment audioCallFragment;
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 Numpad numpad;
private int cameraNumber;
@ -194,7 +195,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
return;
} else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) {
if (LinphoneManager.getLc().getCurrentCall() != null) {
enabledVideoButton(false);
video.setEnabled(false);
}
if (isVideoEnabled(call)) {
showAudioView();
@ -207,7 +208,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
}
}
if (LinphoneManager.getLc().getCurrentCall() != null) {
enabledVideoButton(true);
video.setEnabled(true);
}
} else if (state == State.StreamsRunning) {
switchVideo(isVideoEnabled(call));
@ -344,7 +345,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
//TopBar
video = findViewById(R.id.video);
video.setOnClickListener(this);
enabledVideoButton(false);
video.setEnabled(false);
videoProgress = findViewById(R.id.video_in_progress);
videoProgress.setVisibility(View.GONE);
@ -380,7 +381,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
pause = findViewById(R.id.pause);
pause.setOnClickListener(this);
enabledPauseButton(false);
pause.setEnabled(false);
mActiveCallHeader = findViewById(R.id.active_call);
mNoCurrentCall = findViewById(R.id.no_current_call);
@ -402,6 +403,15 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
conference.setEnabled(false);
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 {
audioRoute = findViewById(R.id.audio_route);
audioRoute.setOnClickListener(this);
@ -489,6 +499,15 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
}
});
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() {
if (!LinphonePreferences.instance().isVideoEnabled() || isConferenceRunning) {
enabledVideoButton(false);
video.setEnabled(false);
} else {
if (video.isEnabled()) {
if (isVideoEnabled(LinphoneManager.getLc().getCurrentCall())) {
@ -582,33 +601,27 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
}
//Enabled transfer button
if (isTransferAllowed && !LinphoneManager.getLc().soundResourcesLocked())
enabledTransferButton(true);
transfer.setEnabled(isTransferAllowed && !LinphoneManager.getLc().soundResourcesLocked());
//Enable conference button
if (LinphoneManager.getLc().getCallsNb() > 1 && LinphoneManager.getLc().getCallsNb() > confsize && !LinphoneManager.getLc().soundResourcesLocked()) {
enabledConferenceButton(true);
} else {
enabledConferenceButton(false);
}
conference.setEnabled(LinphoneManager.getLc().getCallsNb() > 1 && LinphoneManager.getLc().getCallsNb() > confsize && !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()));
if (LinphoneManager.getLc().getCurrentCall() != null && LinphonePreferences.instance().isVideoEnabled() && !LinphoneManager.getLc().getCurrentCall().mediaInProgress()) {
enabledVideoButton(true);
} else {
enabledVideoButton(false);
}
if (LinphoneManager.getLc().getCurrentCall() != null && !LinphoneManager.getLc().getCurrentCall().mediaInProgress()) {
enabledPauseButton(true);
} else {
enabledPauseButton(false);
}
recordCall.setEnabled(!LinphoneManager.getLc().soundResourcesLocked());
recordCall.setImageResource(isRecording ? R.drawable.options_rec_selected : R.drawable.options_rec_default);
recording.setEnabled(isRecording);
recording.setVisibility(isRecording ? View.VISIBLE : View.GONE);
video.setEnabled(LinphoneManager.getLc().getCurrentCall() != null && LinphonePreferences.instance().isVideoEnabled() && !LinphoneManager.getLc().getCurrentCall().mediaInProgress());
pause.setEnabled(LinphoneManager.getLc().getCurrentCall() != null && !LinphoneManager.getLc().getCurrentCall().mediaInProgress());
micro.setEnabled(true);
if (!isTablet()) {
speaker.setEnabled(true);
}
speaker.setEnabled(!isTablet());
transfer.setEnabled(true);
pause.setEnabled(true);
dialer.setEnabled(true);
@ -649,6 +662,17 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
toggleSpeaker();
} else if (id == R.id.add_call) {
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) {
pauseOrResumeCall(LinphoneManager.getLc().getCurrentCall());
} else if (id == R.id.hang_up) {
@ -700,35 +724,31 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
}
}
private void enabledVideoButton(boolean enabled) {
if (enabled) {
video.setEnabled(true);
} else {
video.setEnabled(false);
}
}
private void toggleCallRecording(boolean enable) {
Call call = LinphoneManager.getLc().getCurrentCall();
private void enabledPauseButton(boolean enabled) {
if (enabled) {
pause.setEnabled(true);
} else {
pause.setEnabled(false);
}
}
if (call == null) return;
private void enabledTransferButton(boolean enabled) {
if (enabled) {
transfer.setEnabled(true);
} else {
transfer.setEnabled(false);
}
}
if (enable && !isRecording) {
call.startRecording();
Log.d("start call recording");
private void enabledConferenceButton(boolean enabled) {
if (enabled) {
conference.setEnabled(true);
} else {
conference.setEnabled(false);
recordCall.setImageResource(R.drawable.options_rec_selected);
recording.setVisibility(View.VISIBLE);
recording.setEnabled(true);
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();
Call currentCall = lc.getCurrentCall();
if (isRecording) {
toggleCallRecording(false);
}
if (currentCall != null) {
lc.terminateCall(currentCall);
} else if (lc.isInConference()) {
@ -963,6 +987,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
transfer.setVisibility(View.INVISIBLE);
addCall.setVisibility(View.INVISIBLE);
conference.setVisibility(View.INVISIBLE);
recordCall.setVisibility(View.INVISIBLE);
displayVideoCall(false);
numpad.setVisibility(View.GONE);
options.setSelected(false);
@ -1023,12 +1048,14 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
}
addCall.setVisibility(View.INVISIBLE);
conference.setVisibility(View.INVISIBLE);
recordCall.setVisibility(View.INVISIBLE);
} else { //Display options
if (isTransferAllowed) {
transfer.setVisibility(View.VISIBLE);
}
addCall.setVisibility(View.VISIBLE);
conference.setVisibility(View.VISIBLE);
recordCall.setVisibility(View.VISIBLE);
options.setSelected(true);
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.
*/
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@ -28,6 +29,7 @@ import android.provider.OpenableColumns;
import android.text.TextUtils;
import org.linphone.LinphoneManager;
import org.linphone.core.Address;
import org.linphone.core.ChatMessage;
import org.linphone.core.Content;
import org.linphone.core.Friend;
@ -40,6 +42,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@ -197,6 +200,30 @@ public class FileUtils {
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) {
String appData = message.getAppdata();
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"/>
</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>
<LinearLayout
@ -369,10 +382,21 @@
android:layout_width="match_parent"
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
android:id="@+id/add_call"
android:visibility="gone"
android:layout_above="@id/options"
android:layout_above="@id/record_call"
android:src="@drawable/options_add_call"
android:background="@drawable/button_background"
android:contentDescription="@string/content_description_add_call"

View file

@ -181,6 +181,19 @@
android:layout_gravity="center"/>
</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>
<LinearLayout
@ -310,10 +323,21 @@
android:layout_width="match_parent"
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
android:id="@+id/add_call"
android:visibility="gone"
android:layout_above="@id/options"
android:layout_above="@id/record_call"
android:src="@drawable/options_add_call"
android:background="@drawable/button_background"
android:contentDescription="@string/content_description_add_call"

View file

@ -250,6 +250,7 @@
<!-- Side Menu -->
<string name="menu_assistant">Assistant</string>
<string name="menu_settings">Settings</string>
<string name="menu_recordings">My recordings</string>
<string name="menu_about">About</string>
<string name="quit">Quit</string>
@ -286,6 +287,9 @@
<string name="call_stats_display_filter">Display filter:</string>
<string name="call">Call</string>
<!-- Recordings -->
<string name="no_recordings">No recordings.</string>
<!-- About -->
<string name="menu_send_log">Send 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_description_conversation_subject">Group chat room subject</string>
<string name="content_description_conversation_infos">Group chat room info</string>
<string name="content_description_record_call">Record call</string>
</resources>