From bcb5e2da6627a3cf7d372aaaf06357c1b475d01c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Tue, 20 Nov 2018 16:55:56 +0100 Subject: [PATCH] Add call recording functionality in call view --- .../java/org/linphone/LinphoneActivity.java | 8 ++ .../java/org/linphone/call/CallActivity.java | 133 +++++++++++------- .../java/org/linphone/utils/FileUtils.java | 27 ++++ app/src/main/res/drawable/menu_recordings.png | Bin 0 -> 3142 bytes .../main/res/drawable/options_rec_default.png | Bin 0 -> 3329 bytes .../res/drawable/options_rec_selected.png | Bin 0 -> 3262 bytes app/src/main/res/drawable/recording.png | Bin 0 -> 2103 bytes app/src/main/res/layout-land/call.xml | 26 +++- app/src/main/res/layout/call.xml | 26 +++- app/src/main/res/values/strings.xml | 5 + 10 files changed, 170 insertions(+), 55 deletions(-) create mode 100644 app/src/main/res/drawable/menu_recordings.png create mode 100644 app/src/main/res/drawable/options_rec_default.png create mode 100644 app/src/main/res/drawable/options_rec_selected.png create mode 100644 app/src/main/res/drawable/recording.png diff --git a/app/src/main/java/org/linphone/LinphoneActivity.java b/app/src/main/java/org/linphone/LinphoneActivity.java index 404964729..4de17425a 100644 --- a/app/src/main/java/org/linphone/LinphoneActivity.java +++ b/app/src/main/java/org/linphone/LinphoneActivity.java @@ -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); } }); diff --git a/app/src/main/java/org/linphone/call/CallActivity.java b/app/src/main/java/org/linphone/call/CallActivity.java index dd24a68dd..55649595a 100644 --- a/app/src/main/java/org/linphone/call/CallActivity.java +++ b/app/src/main/java/org/linphone/call/CallActivity.java @@ -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); } diff --git a/app/src/main/java/org/linphone/utils/FileUtils.java b/app/src/main/java/org/linphone/utils/FileUtils.java index 7249b2cab..6eebd6548 100644 --- a/app/src/main/java/org/linphone/utils/FileUtils.java +++ b/app/src/main/java/org/linphone/utils/FileUtils.java @@ -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) { diff --git a/app/src/main/res/drawable/menu_recordings.png b/app/src/main/res/drawable/menu_recordings.png new file mode 100644 index 0000000000000000000000000000000000000000..3bef81d13e85e0209c5d853ec8b3c2ae9f9af5e7 GIT binary patch literal 3142 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/options_rec_selected.png b/app/src/main/res/drawable/options_rec_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef1ca322c960bf9c74f8b58e991035528e76975 GIT binary patch literal 3262 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/recording.png b/app/src/main/res/drawable/recording.png new file mode 100644 index 0000000000000000000000000000000000000000..fb4e826693d98f084e2a417685b2783554086a6c GIT binary patch literal 2103 zcmV-72*~$|P)NLG<-W;8UB+(mL8$>R1D zW-!SOBrh;tG?J_!IVkOwS+8a{Ni>mM7iHxnq(IXFtO3y1uL*!d0CoWQTJ4#W0P02L zoZ55U01N~$0>Eeh;{l8c7|Uq@Pm9Q=xL2nO$+rS#^#_vAksKJ2eksZ2B!3LxQ9-gG zE`j=x+~>pS2+0{F^J3zaM{*{~S|5IUNcL(gldDM{HV|(hxs+t5g!XM`wQ`LH-iJsI zh)TFO$!ed~xgjN^G)f7fqn2dfD0|4Ha@$CDPx;l+gXDG}RV)ZY$Yc~YC8chT>bTit zJe2Mg8BiWziB9fH>Zd0?WUqte% zkdsuS(fW;KQBbG#1}k@c2v6fB)h7)8Ci%Ln=RmT|U=`N{`Et7Jv(%B%-6a2Lqgzx* za-t)@6|_)p4!g9B+dvt#X-zQTRRKqeq?1co_E9POq8t&UlN> z+a~!k)4^(ogKCRrFbYBPevQxphxbrpN}MH|?w?M|0JvFeS83U!(*YEuuIx@D}%sM+_T+`{Cel!dbKD>n(XPkT7dLgf7v4Mqci4=ima zXyv_rUV5p;iF_#~hxSFwIEEOG_1$xoyR^#X7GlNfyptr(1V)Ln<5;Qnsq|LP(<*;- zIQdRY@Z{S>a)O0_KFOc7eoxwB6f+&-1WzX<)O-itvkhmjBE3GEi z#ZkOiXKGkt7~FElINZb-_ShMc;~n{}G8l+~evQ`|!cJw5jJhjkVI#El%9^@HXh$0^ z&tJ1ywcoT3HDLn!B>RzENb<7)L(-(`7usSF8to#I$23}}tm9L+6FRoL>@NeYLXzbq zCo8&>Rk<+Ytw*I{Y!A5-Sa0aOIf`I0-cNmK7}MG?L<-bRooU5-q%2U8&kF4hJ;{pL zcRu&6Whn`Cqv0ZeBo9Wur6)PS=OVdL-JW+&NSFd;NiX;a)W59^dikg#)hy16F^?i& zc?v)H@Y_wYXPh%v5%8`#u7dT0BhnwDR_3Pw9&24SBW!NcQ~+xMTp7^punGn5$EDn) z^CEIq`DWd5DL3gRWOI{71kn2xzycB3lH}$sCb=d?e=q1R_|;UcEy;l--$>vFq(P}# z3EB&4<>rw5ybb58CiO0Q(snS6eNVYKwP>9~I~+hCwU3GboCR97{?(IsSco9uH3Id06Hmm>lm^Z zay6z7VIr&5B%kp+tdaa8qC>mFhu#E|-&*>9PI63Jg!0A-1|%sI$z@uGSP(9fd?MF3 z6H#ri1+bN5enQ8Yc>rDraf-+X@FvN70CP&tIsor@%KZRb2VfyuXS`ewU?zZ#0n9!D z@VvvIPI(0C58xHUk$wq)Pu0<-1leoR)c|s#vdaKekQ`}Jzh`}BZ{p+Hbz*b z*9;d3BYfK4Zcx!F&gv&3wQAS~U7^)AMEU;LB-9v^b3Ns*D(Lu#vM@Q+19;m)zf}E#Fvs)1{-IZWl;t)FHAVe` z&;($b&25T+e1rPMB9sl^0+=r%Upl^jsx582S!oxvx~^#nwbD@_0N5rXkB6ZBr&cZ` z*+oP`QRt<)Zjoq=IEt|@+ee% zUdMfwK3nwsB$pX~57|%0y?8!}k1j>zgh4YNwRj-6YL5?O92m6#SY>dJ<|L$o8?^Ft zk`MZ{U55YOm2DA{b;t(sb66@OtF;*sWIymP(>&*)B2w>FKeYbB;CldD9R#Tqk!^}b zHvr$D^_LAT0Nw?#U0KKy0C(fR35-<$?hNClPd4cL(wGj3FKTpdCV4(2jngEDhIL3k z0q~XfY;kNC%dJ&@pIV92j$Nt&Oc0Typ@b3<0??1$#Q+v35)l&FgY5m*Y5>O#O + + + + + + + + Assistant Settings + My recordings About Quit @@ -286,6 +287,9 @@ Display filter: Call + + No recordings. + Send log Reset log @@ -546,4 +550,5 @@ Linphone instant messages notifications Group chat room subject Group chat room info + Record call