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 01/10] 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 From eb1a3f0e989ae15b416332b3acbe0224be6f0b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Tue, 20 Nov 2018 16:56:51 +0100 Subject: [PATCH 02/10] Set record file before initiating a call --- app/src/main/java/org/linphone/call/CallManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/org/linphone/call/CallManager.java b/app/src/main/java/org/linphone/call/CallManager.java index 956c90537..d72482132 100644 --- a/app/src/main/java/org/linphone/call/CallManager.java +++ b/app/src/main/java/org/linphone/call/CallManager.java @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import org.linphone.LinphoneManager; +import org.linphone.LinphoneUtils; import org.linphone.core.Address; import org.linphone.core.Call; import org.linphone.core.CallParams; @@ -63,6 +64,9 @@ public class CallManager { Log.d("Low bandwidth enabled in call params"); } + String recordFile = LinphoneUtils.getCallRecordingFilename(LinphoneManager.getInstance().getContext(), lAddress); + params.setRecordFile(recordFile); + lc.inviteAddressWithParams(lAddress, params); } From 90774931d766d64c19ee86c9b70834975d1503a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Tue, 20 Nov 2018 16:57:55 +0100 Subject: [PATCH 03/10] Clean acceptCall code and add recordFile in params before accepting a call --- .../java/org/linphone/LinphoneManager.java | 24 ++++++--- .../linphone/call/CallIncomingActivity.java | 12 +---- .../java/org/linphone/views/CallButton.java | 51 ++++++++----------- 3 files changed, 40 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/org/linphone/LinphoneManager.java b/app/src/main/java/org/linphone/LinphoneManager.java index bb0103b36..451ec9fee 100644 --- a/app/src/main/java/org/linphone/LinphoneManager.java +++ b/app/src/main/java/org/linphone/LinphoneManager.java @@ -795,7 +795,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou public void setHandsetMode(Boolean on) { if (mLc.isIncomingInvitePending() && on) { handsetON = true; - mLc.acceptCall(mLc.getCurrentCall()); + acceptCall(mLc.getCurrentCall()); LinphoneActivity.instance().startIncallActivity(); } else if (on && CallActivity.isInstanciated()) { handsetON = true; @@ -1173,7 +1173,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou public void run() { if (mLc != null) { if (mLc.getCallsNb() > 0) { - mLc.acceptCall(call); + acceptCall(call); if (LinphoneManager.getInstance() != null) { LinphoneManager.getInstance().routeAudioToReceiver(); if (LinphoneActivity.instance() != null) @@ -1465,12 +1465,22 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou return reinviteWithVideo(); } - public boolean acceptCallIfIncomingPending() throws CoreException { - if (mLc.isIncomingInvitePending()) { - mLc.acceptCall(mLc.getCurrentCall()); - return true; + public boolean acceptCall(Call call) { + if (call == null) return false; + + CallParams params = LinphoneManager.getLc().createCallParams(call); + + boolean isLowBandwidthConnection = !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance().getApplicationContext()); + + if (params != null) { + params.enableLowBandwidth(isLowBandwidthConnection); + params.setRecordFile(LinphoneUtils.getCallRecordingFilename(getContext(), call.getRemoteAddress())); + } else { + Log.e("Could not create call params for call"); + return false; } - return false; + + return acceptCallWithParams(call, params); } public boolean acceptCallWithParams(Call call, CallParams params) { diff --git a/app/src/main/java/org/linphone/call/CallIncomingActivity.java b/app/src/main/java/org/linphone/call/CallIncomingActivity.java index 1d7b5aec6..05212d482 100644 --- a/app/src/main/java/org/linphone/call/CallIncomingActivity.java +++ b/app/src/main/java/org/linphone/call/CallIncomingActivity.java @@ -246,17 +246,7 @@ public class CallIncomingActivity extends LinphoneGenericActivity { } alreadyAcceptedOrDeniedCall = true; - CallParams params = LinphoneManager.getLc().createCallParams(mCall); - - boolean isLowBandwidthConnection = !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance().getApplicationContext()); - - if (params != null) { - params.enableLowBandwidth(isLowBandwidthConnection); - } else { - Log.e("Could not create call params for call"); - } - - if (params == null || !LinphoneManager.getInstance().acceptCallWithParams(mCall, params)) { + if (!LinphoneManager.getInstance().acceptCall(mCall)) { // the above method takes care of Samsung Galaxy S Toast.makeText(this, R.string.couldnt_accept_call, Toast.LENGTH_LONG).show(); } else { diff --git a/app/src/main/java/org/linphone/views/CallButton.java b/app/src/main/java/org/linphone/views/CallButton.java index e53a660b8..db9201030 100644 --- a/app/src/main/java/org/linphone/views/CallButton.java +++ b/app/src/main/java/org/linphone/views/CallButton.java @@ -58,38 +58,31 @@ public class CallButton extends ImageView implements OnClickListener, AddressAwa } public void onClick(View v) { - try { - if (!LinphoneManager.getInstance().acceptCallIfIncomingPending()) { - if (mAddress.getText().length() > 0) { - LinphoneManager.getInstance().newOutgoingCall(mAddress); - } else { - if (LinphonePreferences.instance().isBisFeatureEnabled()) { - CallLog[] logs = LinphoneManager.getLc().getCallLogs(); - CallLog log = null; - for (CallLog l : logs) { - if (l.getDir() == Call.Dir.Outgoing) { - log = l; - break; - } - } - if (log == null) { - return; - } - - ProxyConfig lpc = LinphoneManager.getLc().getDefaultProxyConfig(); - if (lpc != null && log.getToAddress().getDomain().equals(lpc.getDomain())) { - mAddress.setText(log.getToAddress().getUsername()); - } else { - mAddress.setText(log.getToAddress().asStringUriOnly()); - } - mAddress.setSelection(mAddress.getText().toString().length()); - mAddress.setDisplayedName(log.getToAddress().getDisplayName()); + if (mAddress.getText().length() > 0) { + LinphoneManager.getInstance().newOutgoingCall(mAddress); + } else { + if (LinphonePreferences.instance().isBisFeatureEnabled()) { + CallLog[] logs = LinphoneManager.getLc().getCallLogs(); + CallLog log = null; + for (CallLog l : logs) { + if (l.getDir() == Call.Dir.Outgoing) { + log = l; + break; } } + if (log == null) { + return; + } + + ProxyConfig lpc = LinphoneManager.getLc().getDefaultProxyConfig(); + if (lpc != null && log.getToAddress().getDomain().equals(lpc.getDomain())) { + mAddress.setText(log.getToAddress().getUsername()); + } else { + mAddress.setText(log.getToAddress().asStringUriOnly()); + } + mAddress.setSelection(mAddress.getText().toString().length()); + mAddress.setDisplayedName(log.getToAddress().getDisplayName()); } - } catch (CoreException e) { - LinphoneManager.getInstance().terminateCall(); - onWrongDestinationAddress(); } } From ec2505069ac54f06b73f20347a3a03a324d664f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Mon, 26 Nov 2018 15:18:40 +0100 Subject: [PATCH 04/10] Moved and renamed history --- .../main/java/org/linphone/history/HistoryDetailFragment.java | 1 + app/src/main/java/org/linphone/history/HistoryListFragment.java | 1 + 2 files changed, 2 insertions(+) diff --git a/app/src/main/java/org/linphone/history/HistoryDetailFragment.java b/app/src/main/java/org/linphone/history/HistoryDetailFragment.java index 2ba5b680e..e83c30750 100644 --- a/app/src/main/java/org/linphone/history/HistoryDetailFragment.java +++ b/app/src/main/java/org/linphone/history/HistoryDetailFragment.java @@ -45,6 +45,7 @@ import org.linphone.core.ChatRoomListenerStub; import org.linphone.core.Core; import org.linphone.core.Factory; import org.linphone.core.ProxyConfig; +import org.linphone.fragments.FragmentsAvailable; import org.linphone.mediastream.Log; public class HistoryDetailFragment extends Fragment implements OnClickListener { diff --git a/app/src/main/java/org/linphone/history/HistoryListFragment.java b/app/src/main/java/org/linphone/history/HistoryListFragment.java index cb88b8674..6ef762cd0 100644 --- a/app/src/main/java/org/linphone/history/HistoryListFragment.java +++ b/app/src/main/java/org/linphone/history/HistoryListFragment.java @@ -45,6 +45,7 @@ import org.linphone.core.Call; import org.linphone.core.CallLog; import org.linphone.utils.SelectableHelper; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; From 576d769f46d91d789f95813df3cd4925f48fb7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Mon, 26 Nov 2018 15:19:34 +0100 Subject: [PATCH 05/10] Add recordings list view --- .../java/org/linphone/LinphoneActivity.java | 10 +- .../fragments/FragmentsAvailable.java | 3 +- .../org/linphone/recording/Recording.java | 152 ++++++++++ .../linphone/recording/RecordingAdapter.java | 275 ++++++++++++++++++ .../recording/RecordingListFragment.java | 192 ++++++++++++ .../linphone/recording/RecordingListener.java | 26 ++ .../menu_recordings.png | Bin .../options_rec_default.png | Bin .../options_rec_selected.png | Bin .../main/res/drawable-xhdpi/record_pause.png | Bin 0 -> 5820 bytes .../main/res/drawable-xhdpi/record_play.png | Bin 0 -> 7888 bytes .../recording.png | Bin app/src/main/res/layout/recording_cell.xml | 141 +++++++++ app/src/main/res/layout/recordings.xml | 67 +++++ app/src/main/res/values/strings.xml | 1 + 15 files changed, 863 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/linphone/recording/Recording.java create mode 100644 app/src/main/java/org/linphone/recording/RecordingAdapter.java create mode 100644 app/src/main/java/org/linphone/recording/RecordingListFragment.java create mode 100644 app/src/main/java/org/linphone/recording/RecordingListener.java rename app/src/main/res/{drawable => drawable-xhdpi}/menu_recordings.png (100%) rename app/src/main/res/{drawable => drawable-xhdpi}/options_rec_default.png (100%) rename app/src/main/res/{drawable => drawable-xhdpi}/options_rec_selected.png (100%) create mode 100644 app/src/main/res/drawable-xhdpi/record_pause.png create mode 100644 app/src/main/res/drawable-xhdpi/record_play.png rename app/src/main/res/{drawable => drawable-xhdpi}/recording.png (100%) create mode 100644 app/src/main/res/layout/recording_cell.xml create mode 100644 app/src/main/res/layout/recordings.xml diff --git a/app/src/main/java/org/linphone/LinphoneActivity.java b/app/src/main/java/org/linphone/LinphoneActivity.java index 4de17425a..ab94f84e1 100644 --- a/app/src/main/java/org/linphone/LinphoneActivity.java +++ b/app/src/main/java/org/linphone/LinphoneActivity.java @@ -110,6 +110,7 @@ import org.linphone.purchase.InAppPurchaseActivity; import org.linphone.views.AddressText; import org.linphone.utils.LinphoneGenericActivity; import org.linphone.utils.LinphoneUtils; +import org.linphone.recording.RecordingListFragment; import org.linphone.xmlrpc.XmlRpcHelper; import org.linphone.xmlrpc.XmlRpcListenerBase; @@ -135,7 +136,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick private StatusFragment statusFragment; private TextView missedCalls, missedChats; private RelativeLayout contacts, history, dialer, chat; - private View contacts_selected, history_selected, dialer_selected, chat_selected; + private View contacts_selected, history_selected, dialer_selected, chat_selected, record_selected; private LinearLayout mTopBar; private TextView mTopBarTitle; private ImageView cancel; @@ -428,6 +429,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick case CONTACT_DEVICES: fragment = new DevicesFragment(); break; + case RECORDING_LIST: + fragment = new RecordingListFragment(); + break; default: break; } @@ -638,7 +642,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick } public void displayRecordings() { - + changeCurrentFragment(FragmentsAvailable.RECORDING_LIST, null); } public void displayContactsForEdition(String sipAddress, String displayName) { @@ -1634,7 +1638,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick LinphoneActivity.instance().displayInapp(); } } - if (sideMenuItemList.getAdapter().getItem(i).toString().equals(R.string.menu_recordings)) { + if (sideMenuItemList.getAdapter().getItem(i).toString().equals(getString(R.string.menu_recordings))) { LinphoneActivity.instance().displayRecordings(); } openOrCloseSideMenu(false); diff --git a/app/src/main/java/org/linphone/fragments/FragmentsAvailable.java b/app/src/main/java/org/linphone/fragments/FragmentsAvailable.java index d59fc5df0..9ed1af3da 100644 --- a/app/src/main/java/org/linphone/fragments/FragmentsAvailable.java +++ b/app/src/main/java/org/linphone/fragments/FragmentsAvailable.java @@ -37,7 +37,8 @@ public enum FragmentsAvailable { INFO_GROUP_CHAT, GROUP_CHAT, MESSAGE_IMDN, - CONTACT_DEVICES; + CONTACT_DEVICES, + RECORDING_LIST; public boolean shouldAddItselfToTheRightOf(FragmentsAvailable fragment) { switch (this) { diff --git a/app/src/main/java/org/linphone/recording/Recording.java b/app/src/main/java/org/linphone/recording/Recording.java new file mode 100644 index 000000000..588360772 --- /dev/null +++ b/app/src/main/java/org/linphone/recording/Recording.java @@ -0,0 +1,152 @@ +package org.linphone.recording; + +/* +Recording.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +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 2 +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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.annotation.SuppressLint; +import android.support.annotation.NonNull; + +import org.linphone.LinphoneManager; +import org.linphone.core.Player; +import org.linphone.core.PlayerListener; +import org.linphone.mediastream.Log; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Recording implements PlayerListener, Comparable { + private String recordPath, name; + private Date recordDate; + private Player player; + private RecordingListener listener; + private Timer timer; + private TimerTask updateCurrentPositionTimer; + + private static final Pattern mRecordPattern = Pattern.compile(".*/(.*)_(\\d{2}-\\d{2}-\\d{4}-\\d{2}-\\d{2}-\\d{2})\\..*"); + + @SuppressLint("SimpleDateFormat") + public Recording(String recordPath) { + this.recordPath = recordPath; + + Matcher m = mRecordPattern.matcher(recordPath); + if (m.matches()) { + name = m.group(1); + + try { + recordDate = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss").parse(m.group(2)); + } catch (ParseException e) { + Log.e(e); + } + } + + timer = new Timer(); + updateCurrentPositionTimer = new TimerTask() { + @Override + public void run() { + if (listener != null) listener.currentPositionChanged(getCurrentPosition()); + } + }; + + player = LinphoneManager.getLc().createLocalPlayer(null, null, null); + player.setListener(this); + player.open(recordPath); + } + + public String getRecordPath() { + return recordPath; + } + + public String getName() { + return name; + } + + public Date getRecordDate() { + return recordDate; + } + + public boolean isClosed() { + return player.getState() == Player.State.Closed; + } + + public void play() { + player.start(); + //timer.scheduleAtFixedRate(updateCurrentPositionTimer, 0, 1000); + } + + public boolean isPlaying() { + return player.getState() == Player.State.Playing; + } + + public void pause() { + if (!isClosed()) { + player.pause(); + + timer.cancel(); + timer.purge(); + } + } + + public boolean isPaused() { + return player.getState() == Player.State.Paused; + } + + public void seek(int i) { + if (!isClosed()) player.seek(i); + } + + public int getCurrentPosition() { + return player.getCurrentPosition(); + } + + public int getDuration() { + return player.getDuration(); + } + + public void close() { + player.close(); + } + + public void setRecordingListener(RecordingListener listener) { + this.listener = listener; + } + + @Override + public void onEofReached(Player player) { + if (listener != null) listener.endOfRecordReached(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Recording) { + Recording r = (Recording) o; + return recordPath.equals(r.getRecordPath()); + } + return false; + } + + @Override + public int compareTo(@NonNull Recording o) { + return -recordDate.compareTo(o.getRecordDate()); + } +} diff --git a/app/src/main/java/org/linphone/recording/RecordingAdapter.java b/app/src/main/java/org/linphone/recording/RecordingAdapter.java new file mode 100644 index 000000000..5222b3ef0 --- /dev/null +++ b/app/src/main/java/org/linphone/recording/RecordingAdapter.java @@ -0,0 +1,275 @@ +package org.linphone.recording; + +/* +RecordingAdapter.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +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 2 +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, 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.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +import org.linphone.R; +import org.linphone.mediastream.Log; +import org.linphone.ui.SelectableAdapter; +import org.linphone.ui.SelectableHelper; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class RecordingAdapter extends SelectableAdapter { + + public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + public ImageView playButton; + public TextView name, date, currentPosition, duration; + public SeekBar progressionBar; + public CheckBox select; + public LinearLayout separator; + public TextView separatorText; + private RecordingAdapter.ViewHolder.ClickListener listener; + + public ViewHolder(View view, RecordingAdapter.ViewHolder.ClickListener listener) { + super(view); + + playButton = view.findViewById(R.id.record_play); + name = view.findViewById(R.id.record_name); + date = view.findViewById(R.id.record_date); + currentPosition = view.findViewById(R.id.record_current_time); + duration = view.findViewById(R.id.record_duration); + progressionBar = view.findViewById(R.id.record_progression_bar); + select = view.findViewById(R.id.delete); + separator = view.findViewById(R.id.separator); + separatorText = view.findViewById(R.id.separator_text); + + this.listener = listener; + view.setOnClickListener(this); + view.setOnLongClickListener(this); + } + + @Override + public void onClick(View view) { + if (listener != null) { + listener.onItemClicked(getAdapterPosition()); + } + } + + @Override + public boolean onLongClick(View view) { + if (listener != null) { + return listener.onItemLongClicked(getAdapterPosition()); + } + return false; + } + + public interface ClickListener { + void onItemClicked(int position); + + boolean onItemLongClicked(int position); + } + } + + private List recordings; + private Context context; + private RecordingAdapter.ViewHolder.ClickListener clickListener; + + public RecordingAdapter(Context context, List recordings, RecordingAdapter.ViewHolder.ClickListener listener, SelectableHelper helper) { + super(helper); + + this.recordings = recordings; + this.context = context; + this.clickListener = listener; + } + + @Override + public Object getItem(int position) { + return recordings.get(position); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recording_cell, viewGroup, false); + return new RecordingAdapter.ViewHolder(v, clickListener); + } + + @SuppressLint("SimpleDateFormat") + @Override + public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int i) { + final Recording record = recordings.get(i); + + viewHolder.name.setSelected(true); // For automated horizontal scrolling of long texts + + Calendar recordTime = Calendar.getInstance(); + recordTime.setTime(record.getRecordDate()); + viewHolder.separatorText.setText(DateToHumanDate(recordTime)); + viewHolder.select.setVisibility(isEditionEnabled() ? View.VISIBLE : View.GONE); + viewHolder.select.setChecked(isSelected(i)); + + if (i > 0) { + Recording previousRecord = recordings.get(i - 1); + Date previousRecordDate = previousRecord.getRecordDate(); + Calendar previousRecordTime = Calendar.getInstance(); + previousRecordTime.setTime(previousRecordDate); + + if (isSameDay(previousRecordTime, recordTime)) { + viewHolder.separator.setVisibility(View.GONE); + } else { + viewHolder.separator.setVisibility(View.VISIBLE); + } + } else { + viewHolder.separator.setVisibility(View.VISIBLE); + } + + if (record.isPlaying()) { + viewHolder.playButton.setImageResource(R.drawable.record_pause); + } else { + viewHolder.playButton.setImageResource(R.drawable.record_play); + } + viewHolder.playButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (record.isPaused()) { + record.play(); + viewHolder.playButton.setImageResource(R.drawable.record_pause); + } else { + record.pause(); + viewHolder.playButton.setImageResource(R.drawable.record_play); + } + } + }); + + viewHolder.name.setText(record.getName()); + viewHolder.date.setText(new SimpleDateFormat("HH:mm").format(record.getRecordDate())); + + int position = record.getCurrentPosition(); + viewHolder.currentPosition.setText(String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(position), + TimeUnit.MILLISECONDS.toSeconds(position) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(position)) + )); + //viewHolder.currentPosition.setText("00:00"); + + int duration = record.getDuration(); + viewHolder.duration.setText(String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(duration), + TimeUnit.MILLISECONDS.toSeconds(duration) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)) + )); + + viewHolder.progressionBar.setMax(100); + viewHolder.progressionBar.setProgress(0); + viewHolder.progressionBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + if (progress == 0) { + record.seek(0); + } else if (progress == seekBar.getMax()) { + if (record.isPlaying()) record.pause(); + record.seek(0); + seekBar.setProgress(0); + } else { + record.seek(progress); + + int currentPosition = record.getCurrentPosition(); + viewHolder.currentPosition.setText(String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(currentPosition), + TimeUnit.MILLISECONDS.toSeconds(currentPosition) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(currentPosition)) + )); + } + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + }); + + record.setRecordingListener(new RecordingListener() { + @Override + public void currentPositionChanged(int currentPosition) { + viewHolder.currentPosition.setText(String.format("%02d:%02", + currentPosition % 60, + currentPosition - (currentPosition % 60) * 60, Locale.getDefault())); + viewHolder.progressionBar.setProgress(currentPosition); + } + + @Override + public void endOfRecordReached() { + record.pause(); + record.seek(0); + viewHolder.progressionBar.setProgress(0); + } + }); + } + + @Override + public int getItemCount() { + return recordings.size(); + } + + @SuppressLint("SimpleDateFormat") + private String DateToHumanDate(Calendar cal) { + SimpleDateFormat dateFormat; + if (isToday(cal)) { + return context.getString(R.string.today); + } else if (isYesterday(cal)) { + return context.getString(R.string.yesterday); + } else { + dateFormat = new SimpleDateFormat(context.getResources().getString(R.string.history_date_format)); + } + + return dateFormat.format(cal.getTime()); + } + + private boolean isSameDay(Calendar cal1, Calendar cal2) { + if (cal1 == null || cal2 == null) { + return false; + } + + return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && + cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)); + } + + private boolean isToday(Calendar cal) { + return isSameDay(cal, Calendar.getInstance()); + } + + private boolean isYesterday(Calendar cal) { + Calendar yesterday = Calendar.getInstance(); + yesterday.roll(Calendar.DAY_OF_MONTH, -1); + return isSameDay(cal, yesterday); + } +} diff --git a/app/src/main/java/org/linphone/recording/RecordingListFragment.java b/app/src/main/java/org/linphone/recording/RecordingListFragment.java new file mode 100644 index 000000000..0a17aa3c7 --- /dev/null +++ b/app/src/main/java/org/linphone/recording/RecordingListFragment.java @@ -0,0 +1,192 @@ +package org.linphone.recording; + +/* +RecordingListFragment.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +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 2 +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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.app.Fragment; +import android.content.Context; +import android.os.Bundle; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.TextView; + +import org.linphone.LinphoneUtils; +import org.linphone.R; +import org.linphone.activities.LinphoneActivity; +import org.linphone.fragments.FragmentsAvailable; +import org.linphone.mediastream.Log; +import org.linphone.ui.SelectableHelper; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RecordingListFragment extends Fragment implements View.OnClickListener, AdapterView.OnItemClickListener, RecordingAdapter.ViewHolder.ClickListener, SelectableHelper.DeleteListener { + private RecyclerView recordingList; + private List recordings; + private TextView noRecordings; + private RecordingAdapter recordingAdapter; + private LinearLayoutManager layoutManager; + private Context context; + private SelectableHelper selectableHelper; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.recordings, container, false); + + context = getActivity().getApplicationContext(); + selectableHelper = new SelectableHelper(view, this); + + recordingList = view.findViewById(R.id.recording_list); + noRecordings = view.findViewById(R.id.no_recordings); + + layoutManager = new LinearLayoutManager(context); + recordingList.setLayoutManager(layoutManager); + + //Divider between items + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recordingList.getContext(), + layoutManager.getOrientation()); + dividerItemDecoration.setDrawable(context.getResources().getDrawable(R.drawable.divider)); + recordingList.addItemDecoration(dividerItemDecoration); + + recordings = new ArrayList<>(); + + return view; + } + + private void hideRecordingListAndDisplayMessageIfEmpty() { + if (recordings == null || recordings.isEmpty()) { + noRecordings.setVisibility(View.VISIBLE); + recordingList.setVisibility(View.GONE); + } else { + noRecordings.setVisibility(View.GONE); + recordingList.setVisibility(View.VISIBLE); + } + } + + public void searchForRecordings() { + String recordingsDirectory = LinphoneUtils.getRecordingsDirectory(context); + File directory = new File(recordingsDirectory); + + if (directory.exists() && directory.isDirectory()) { + File[] existingRecordings = directory.listFiles(); + + for (File f : existingRecordings) { + boolean exists = false; + for(Recording r : recordings) { + if (r.getRecordPath().equals(f.getPath())) { + exists = true; + break; + } + } + + if (!exists) recordings.add(new Recording(f.getPath())); + } + + Collections.sort(recordings); + } + } + + @Override + public void onResume() { + super.onResume(); + + if (LinphoneActivity.isInstanciated()) { + LinphoneActivity.instance().selectMenu(FragmentsAvailable.RECORDING_LIST); + } + + searchForRecordings(); + + hideRecordingListAndDisplayMessageIfEmpty(); + recordingAdapter = new RecordingAdapter(getActivity().getApplicationContext(), recordings, this, selectableHelper); + recordingList.setAdapter(recordingAdapter); + selectableHelper.setAdapter(recordingAdapter); + selectableHelper.setDialogMessage(R.string.recordings_delete_dialog); + } + + @Override + public void onPause() { + super.onPause(); + + // Close all opened recordings + for (Recording r : recordings) { + if (!r.isClosed()) { + if (r.isPlaying()) r.pause(); + r.close(); + } + } + } + + @Override + public void onClick(View v) { + + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (recordingAdapter.isEditionEnabled()) { + Recording record = recordings.get(position); + recordings.remove(position); + + if (record.isPlaying()) record.pause(); + record.close(); + + File recordingFile = new File(record.getRecordPath()); + recordingFile.delete(); + } + } + + @Override + public void onItemClicked(int position) { + if (recordingAdapter.isEditionEnabled()) { + recordingAdapter.toggleSelection(position); + } + } + + @Override + public boolean onItemLongClicked(int position) { + if (!recordingAdapter.isEditionEnabled()) { + selectableHelper.enterEditionMode(); + } + recordingAdapter.toggleSelection(position); + return true; + } + + @Override + public void onDeleteSelection(Object[] objectsToDelete) { + int size = recordingAdapter.getSelectedItemCount(); + for (int i = 0; i < size; i++) { + Recording record = (Recording) objectsToDelete[i]; + recordings.remove(record); + + if (record.isPlaying()) record.pause(); + record.close(); + + File recordingFile = new File(record.getRecordPath()); + recordingFile.delete(); + } + } +} diff --git a/app/src/main/java/org/linphone/recording/RecordingListener.java b/app/src/main/java/org/linphone/recording/RecordingListener.java new file mode 100644 index 000000000..8853d8c14 --- /dev/null +++ b/app/src/main/java/org/linphone/recording/RecordingListener.java @@ -0,0 +1,26 @@ +package org.linphone.recording; + +/* +RecordingListener.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +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 2 +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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +public interface RecordingListener { + void currentPositionChanged(int currentPosition); + + void endOfRecordReached(); +} diff --git a/app/src/main/res/drawable/menu_recordings.png b/app/src/main/res/drawable-xhdpi/menu_recordings.png similarity index 100% rename from app/src/main/res/drawable/menu_recordings.png rename to app/src/main/res/drawable-xhdpi/menu_recordings.png diff --git a/app/src/main/res/drawable/options_rec_default.png b/app/src/main/res/drawable-xhdpi/options_rec_default.png similarity index 100% rename from app/src/main/res/drawable/options_rec_default.png rename to app/src/main/res/drawable-xhdpi/options_rec_default.png diff --git a/app/src/main/res/drawable/options_rec_selected.png b/app/src/main/res/drawable-xhdpi/options_rec_selected.png similarity index 100% rename from app/src/main/res/drawable/options_rec_selected.png rename to app/src/main/res/drawable-xhdpi/options_rec_selected.png diff --git a/app/src/main/res/drawable-xhdpi/record_pause.png b/app/src/main/res/drawable-xhdpi/record_pause.png new file mode 100644 index 0000000000000000000000000000000000000000..3b9311e43b9e8f5475e229ede9f97569c3710409 GIT binary patch literal 5820 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/record_play.png b/app/src/main/res/drawable-xhdpi/record_play.png new file mode 100644 index 0000000000000000000000000000000000000000..486dfb0de0dd04b85846f36e0ded1e764f828396 GIT binary patch literal 7888 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 Q2>c`Nb=Xt6>*#O)2f>lKasU7T literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/recording.png b/app/src/main/res/drawable-xhdpi/recording.png similarity index 100% rename from app/src/main/res/drawable/recording.png rename to app/src/main/res/drawable-xhdpi/recording.png diff --git a/app/src/main/res/layout/recording_cell.xml b/app/src/main/res/layout/recording_cell.xml new file mode 100644 index 000000000..f3a477de8 --- /dev/null +++ b/app/src/main/res/layout/recording_cell.xml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recordings.xml b/app/src/main/res/layout/recordings.xml new file mode 100644 index 000000000..a76d1297d --- /dev/null +++ b/app/src/main/res/layout/recordings.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7ad50480..747cd551b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -289,6 +289,7 @@ No recordings. + Do you really want to delete and leave the selected recordings? Send log From e9fccf4ccdad2259cd5263749ae97e05b5dd92b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Mon, 26 Nov 2018 16:05:32 +0100 Subject: [PATCH 06/10] Fix conflicts after rebase --- .../java/org/linphone/LinphoneManager.java | 3 +- .../java/org/linphone/call/CallManager.java | 4 +- .../linphone/recording/RecordingAdapter.java | 84 ++++--------------- .../recording/RecordingListFragment.java | 11 ++- .../recording/RecordingViewHolder.java | 80 ++++++++++++++++++ 5 files changed, 103 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/org/linphone/recording/RecordingViewHolder.java diff --git a/app/src/main/java/org/linphone/LinphoneManager.java b/app/src/main/java/org/linphone/LinphoneManager.java index 451ec9fee..0915e611c 100644 --- a/app/src/main/java/org/linphone/LinphoneManager.java +++ b/app/src/main/java/org/linphone/LinphoneManager.java @@ -111,6 +111,7 @@ import org.linphone.receivers.KeepAliveReceiver; import org.linphone.receivers.NetworkManager; import org.linphone.receivers.OutgoingCallReceiver; import org.linphone.settings.LinphonePreferences; +import org.linphone.utils.FileUtils; import org.linphone.utils.LinphoneMediaScanner; import org.linphone.utils.LinphoneUtils; @@ -1474,7 +1475,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou if (params != null) { params.enableLowBandwidth(isLowBandwidthConnection); - params.setRecordFile(LinphoneUtils.getCallRecordingFilename(getContext(), call.getRemoteAddress())); + params.setRecordFile(FileUtils.getCallRecordingFilename(getContext(), call.getRemoteAddress())); } else { Log.e("Could not create call params for call"); return false; diff --git a/app/src/main/java/org/linphone/call/CallManager.java b/app/src/main/java/org/linphone/call/CallManager.java index d72482132..47b087e15 100644 --- a/app/src/main/java/org/linphone/call/CallManager.java +++ b/app/src/main/java/org/linphone/call/CallManager.java @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import org.linphone.LinphoneManager; -import org.linphone.LinphoneUtils; +import org.linphone.utils.FileUtils; import org.linphone.core.Address; import org.linphone.core.Call; import org.linphone.core.CallParams; @@ -64,7 +64,7 @@ public class CallManager { Log.d("Low bandwidth enabled in call params"); } - String recordFile = LinphoneUtils.getCallRecordingFilename(LinphoneManager.getInstance().getContext(), lAddress); + String recordFile = FileUtils.getCallRecordingFilename(LinphoneManager.getInstance().getContext(), lAddress); params.setRecordFile(recordFile); lc.inviteAddressWithParams(lAddress, params); diff --git a/app/src/main/java/org/linphone/recording/RecordingAdapter.java b/app/src/main/java/org/linphone/recording/RecordingAdapter.java index 5222b3ef0..04e4ccbd7 100644 --- a/app/src/main/java/org/linphone/recording/RecordingAdapter.java +++ b/app/src/main/java/org/linphone/recording/RecordingAdapter.java @@ -22,20 +22,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import android.annotation.SuppressLint; import android.content.Context; import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.SeekBar; -import android.widget.TextView; import org.linphone.R; -import org.linphone.mediastream.Log; -import org.linphone.ui.SelectableAdapter; -import org.linphone.ui.SelectableHelper; +import org.linphone.utils.SelectableAdapter; +import org.linphone.utils.SelectableHelper; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -44,62 +38,12 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; -public class RecordingAdapter extends SelectableAdapter { - - public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { - public ImageView playButton; - public TextView name, date, currentPosition, duration; - public SeekBar progressionBar; - public CheckBox select; - public LinearLayout separator; - public TextView separatorText; - private RecordingAdapter.ViewHolder.ClickListener listener; - - public ViewHolder(View view, RecordingAdapter.ViewHolder.ClickListener listener) { - super(view); - - playButton = view.findViewById(R.id.record_play); - name = view.findViewById(R.id.record_name); - date = view.findViewById(R.id.record_date); - currentPosition = view.findViewById(R.id.record_current_time); - duration = view.findViewById(R.id.record_duration); - progressionBar = view.findViewById(R.id.record_progression_bar); - select = view.findViewById(R.id.delete); - separator = view.findViewById(R.id.separator); - separatorText = view.findViewById(R.id.separator_text); - - this.listener = listener; - view.setOnClickListener(this); - view.setOnLongClickListener(this); - } - - @Override - public void onClick(View view) { - if (listener != null) { - listener.onItemClicked(getAdapterPosition()); - } - } - - @Override - public boolean onLongClick(View view) { - if (listener != null) { - return listener.onItemLongClicked(getAdapterPosition()); - } - return false; - } - - public interface ClickListener { - void onItemClicked(int position); - - boolean onItemLongClicked(int position); - } - } - +public class RecordingAdapter extends SelectableAdapter { private List recordings; private Context context; - private RecordingAdapter.ViewHolder.ClickListener clickListener; + private RecordingViewHolder.ClickListener clickListener; - public RecordingAdapter(Context context, List recordings, RecordingAdapter.ViewHolder.ClickListener listener, SelectableHelper helper) { + public RecordingAdapter(Context context, List recordings, RecordingViewHolder.ClickListener listener, SelectableHelper helper) { super(helper); this.recordings = recordings; @@ -114,14 +58,14 @@ public class RecordingAdapter extends SelectableAdapter recordings; private TextView noRecordings; @@ -88,7 +87,7 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe } public void searchForRecordings() { - String recordingsDirectory = LinphoneUtils.getRecordingsDirectory(context); + String recordingsDirectory = FileUtils.getRecordingsDirectory(context); File directory = new File(recordingsDirectory); if (directory.exists() && directory.isDirectory()) { diff --git a/app/src/main/java/org/linphone/recording/RecordingViewHolder.java b/app/src/main/java/org/linphone/recording/RecordingViewHolder.java new file mode 100644 index 000000000..7e3389038 --- /dev/null +++ b/app/src/main/java/org/linphone/recording/RecordingViewHolder.java @@ -0,0 +1,80 @@ +package org.linphone.recording; + + +/* + RecordingViewHolder.java + Copyright (C) 2018 Belledonne Communications, Grenoble, France + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.SeekBar; +import android.widget.TextView; + +import org.linphone.R; + +public class RecordingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + public ImageView playButton; + public TextView name, date, currentPosition, duration; + public SeekBar progressionBar; + public CheckBox select; + public LinearLayout separator; + public TextView separatorText; + private RecordingViewHolder.ClickListener listener; + + public RecordingViewHolder(View view, RecordingViewHolder.ClickListener listener) { + super(view); + + playButton = view.findViewById(R.id.record_play); + name = view.findViewById(R.id.record_name); + date = view.findViewById(R.id.record_date); + currentPosition = view.findViewById(R.id.record_current_time); + duration = view.findViewById(R.id.record_duration); + progressionBar = view.findViewById(R.id.record_progression_bar); + select = view.findViewById(R.id.delete); + separator = view.findViewById(R.id.separator); + separatorText = view.findViewById(R.id.separator_text); + + this.listener = listener; + view.setOnClickListener(this); + view.setOnLongClickListener(this); + } + + @Override + public void onClick(View view) { + if (listener != null) { + listener.onItemClicked(getAdapterPosition()); + } + } + + @Override + public boolean onLongClick(View view) { + if (listener != null) { + return listener.onItemLongClicked(getAdapterPosition()); + } + return false; + } + + public interface ClickListener { + void onItemClicked(int position); + + boolean onItemLongClicked(int position); + } +} From a9c730e26b2a8cc9fcc2b930cc23a2e407ad845b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Thu, 29 Nov 2018 14:14:25 +0100 Subject: [PATCH 07/10] Fix various bugs --- .../java/org/linphone/LinphoneManager.java | 4 ++ .../org/linphone/recording/Recording.java | 37 ++++++++------ .../linphone/recording/RecordingAdapter.java | 48 +++++++++--------- .../recording/RecordingListFragment.java | 49 ++++++++++++++++--- app/src/main/res/layout/recording_cell.xml | 3 +- 5 files changed, 95 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/org/linphone/LinphoneManager.java b/app/src/main/java/org/linphone/LinphoneManager.java index 0915e611c..f364cfbe8 100644 --- a/app/src/main/java/org/linphone/LinphoneManager.java +++ b/app/src/main/java/org/linphone/LinphoneManager.java @@ -1143,6 +1143,10 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou return null; } + public void setAudioManagerModeNormal() { + mAudioManager.setMode(AudioManager.MODE_NORMAL); + } + public void setAudioManagerInCallMode() { if (mAudioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) { Log.w("[AudioManager] already in MODE_IN_COMMUNICATION, skipping..."); diff --git a/app/src/main/java/org/linphone/recording/Recording.java b/app/src/main/java/org/linphone/recording/Recording.java index 588360772..bb8d75514 100644 --- a/app/src/main/java/org/linphone/recording/Recording.java +++ b/app/src/main/java/org/linphone/recording/Recording.java @@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Handler; import android.support.annotation.NonNull; import org.linphone.LinphoneManager; @@ -30,8 +32,6 @@ import org.linphone.mediastream.Log; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.Timer; -import java.util.TimerTask; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -40,16 +40,16 @@ public class Recording implements PlayerListener, Comparable { private Date recordDate; private Player player; private RecordingListener listener; - private Timer timer; - private TimerTask updateCurrentPositionTimer; + private Handler handler; + private Runnable updateCurrentPositionTimer; - private static final Pattern mRecordPattern = Pattern.compile(".*/(.*)_(\\d{2}-\\d{2}-\\d{4}-\\d{2}-\\d{2}-\\d{2})\\..*"); + public static final Pattern RECORD_PATTERN = Pattern.compile(".*/(.*)_(\\d{2}-\\d{2}-\\d{4}-\\d{2}-\\d{2}-\\d{2})\\..*"); @SuppressLint("SimpleDateFormat") - public Recording(String recordPath) { + public Recording(Context context, String recordPath) { this.recordPath = recordPath; - Matcher m = mRecordPattern.matcher(recordPath); + Matcher m = RECORD_PATTERN.matcher(recordPath); if (m.matches()) { name = m.group(1); @@ -60,17 +60,17 @@ public class Recording implements PlayerListener, Comparable { } } - timer = new Timer(); - updateCurrentPositionTimer = new TimerTask() { + handler = new Handler(context.getMainLooper()); + updateCurrentPositionTimer = new Runnable() { @Override public void run() { if (listener != null) listener.currentPositionChanged(getCurrentPosition()); + if (isPlaying()) handler.postDelayed(updateCurrentPositionTimer, 20); } }; player = LinphoneManager.getLc().createLocalPlayer(null, null, null); player.setListener(this); - player.open(recordPath); } public String getRecordPath() { @@ -90,8 +90,12 @@ public class Recording implements PlayerListener, Comparable { } public void play() { + if (isClosed()) { + player.open(recordPath); + } + player.start(); - //timer.scheduleAtFixedRate(updateCurrentPositionTimer, 0, 1000); + handler.post(updateCurrentPositionTimer); } public boolean isPlaying() { @@ -101,9 +105,6 @@ public class Recording implements PlayerListener, Comparable { public void pause() { if (!isClosed()) { player.pause(); - - timer.cancel(); - timer.purge(); } } @@ -116,10 +117,18 @@ public class Recording implements PlayerListener, Comparable { } public int getCurrentPosition() { + if (isClosed()) { + player.open(recordPath); + } + return player.getCurrentPosition(); } public int getDuration() { + if (isClosed()) { + player.open(recordPath); + } + return player.getDuration(); } diff --git a/app/src/main/java/org/linphone/recording/RecordingAdapter.java b/app/src/main/java/org/linphone/recording/RecordingAdapter.java index 04e4ccbd7..3a62fc77a 100644 --- a/app/src/main/java/org/linphone/recording/RecordingAdapter.java +++ b/app/src/main/java/org/linphone/recording/RecordingAdapter.java @@ -35,7 +35,6 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.concurrent.TimeUnit; public class RecordingAdapter extends SelectableAdapter { @@ -112,12 +111,11 @@ public class RecordingAdapter extends SelectableAdapter { viewHolder.name.setText(record.getName()); viewHolder.date.setText(new SimpleDateFormat("HH:mm").format(record.getRecordDate())); -// int position = record.getCurrentPosition(); -// viewHolder.currentPosition.setText(String.format("%02d:%02d", -// TimeUnit.MILLISECONDS.toMinutes(position), -// TimeUnit.MILLISECONDS.toSeconds(position) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(position)) -// )); - viewHolder.currentPosition.setText("00:00"); + int position = record.getCurrentPosition(); + viewHolder.currentPosition.setText(String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(position), + TimeUnit.MILLISECONDS.toSeconds(position) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(position)) + )); int duration = record.getDuration(); viewHolder.duration.setText(String.format("%02d:%02d", @@ -125,27 +123,26 @@ public class RecordingAdapter extends SelectableAdapter { TimeUnit.MILLISECONDS.toSeconds(duration) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)) )); - viewHolder.progressionBar.setMax(100); + viewHolder.progressionBar.setMax(record.getDuration()); viewHolder.progressionBar.setProgress(0); viewHolder.progressionBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { - if (progress == 0) { - record.seek(0); - } else if (progress == seekBar.getMax()) { - if (record.isPlaying()) record.pause(); - record.seek(0); - seekBar.setProgress(0); - } else { - record.seek(progress); + int progressToSet = progress > 0 && progress < seekBar.getMax() ? progress : 0; - int currentPosition = record.getCurrentPosition(); - viewHolder.currentPosition.setText(String.format("%02d:%02d", - TimeUnit.MILLISECONDS.toMinutes(currentPosition), - TimeUnit.MILLISECONDS.toSeconds(currentPosition) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(currentPosition)) - )); + if (progress == seekBar.getMax()) { + if (record.isPlaying()) record.pause(); } + + record.seek(progressToSet); + seekBar.setProgress(progressToSet); + + int currentPosition = record.getCurrentPosition(); + viewHolder.currentPosition.setText(String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(currentPosition), + TimeUnit.MILLISECONDS.toSeconds(currentPosition) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(currentPosition)) + )); } } @@ -163,9 +160,10 @@ public class RecordingAdapter extends SelectableAdapter { record.setRecordingListener(new RecordingListener() { @Override public void currentPositionChanged(int currentPosition) { - viewHolder.currentPosition.setText(String.format("%02d:%02", - currentPosition % 60, - currentPosition - (currentPosition % 60) * 60, Locale.getDefault())); + viewHolder.currentPosition.setText(String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(currentPosition), + TimeUnit.MILLISECONDS.toSeconds(currentPosition) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(currentPosition)) + )); viewHolder.progressionBar.setProgress(currentPosition); } @@ -174,6 +172,8 @@ public class RecordingAdapter extends SelectableAdapter { record.pause(); record.seek(0); viewHolder.progressionBar.setProgress(0); + viewHolder.currentPosition.setText("00:00"); + viewHolder.playButton.setImageResource(R.drawable.record_play); } }); } diff --git a/app/src/main/java/org/linphone/recording/RecordingListFragment.java b/app/src/main/java/org/linphone/recording/RecordingListFragment.java index ed6eea250..e4b2ddfc6 100644 --- a/app/src/main/java/org/linphone/recording/RecordingListFragment.java +++ b/app/src/main/java/org/linphone/recording/RecordingListFragment.java @@ -32,8 +32,10 @@ import android.widget.AdapterView; import android.widget.TextView; import org.linphone.LinphoneActivity; +import org.linphone.LinphoneManager; import org.linphone.R; import org.linphone.fragments.FragmentsAvailable; +import org.linphone.mediastream.Log; import org.linphone.utils.FileUtils; import org.linphone.utils.SelectableHelper; @@ -86,6 +88,29 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe } } + public void removeDeletedRecordings() { + String recordingsDirectory = FileUtils.getRecordingsDirectory(context); + File directory = new File(recordingsDirectory); + + if (directory.exists() && directory.isDirectory()) { + File[] existingRecordings = directory.listFiles(); + + for(Recording r : recordings) { + boolean exists = false; + for(File f : existingRecordings) { + if (f.getPath().equals(r.getRecordPath())) { + exists = true; + break; + } + } + + if (!exists) recordings.remove(r); + } + + Collections.sort(recordings); + } + } + public void searchForRecordings() { String recordingsDirectory = FileUtils.getRecordingsDirectory(context); File directory = new File(recordingsDirectory); @@ -93,7 +118,7 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe if (directory.exists() && directory.isDirectory()) { File[] existingRecordings = directory.listFiles(); - for (File f : existingRecordings) { + for(File f : existingRecordings) { boolean exists = false; for(Recording r : recordings) { if (r.getRecordPath().equals(f.getPath())) { @@ -102,7 +127,11 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe } } - if (!exists) recordings.add(new Recording(f.getPath())); + if (!exists) { + if (Recording.RECORD_PATTERN.matcher(f.getPath()).matches()) { + recordings.add(new Recording(context, f.getPath())); + } + } } Collections.sort(recordings); @@ -113,10 +142,14 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe public void onResume() { super.onResume(); + LinphoneManager.getInstance().setAudioManagerModeNormal(); + LinphoneManager.getInstance().routeAudioToSpeaker(); + if (LinphoneActivity.isInstanciated()) { LinphoneActivity.instance().selectMenu(FragmentsAvailable.RECORDING_LIST); } + removeDeletedRecordings(); searchForRecordings(); hideRecordingListAndDisplayMessageIfEmpty(); @@ -130,6 +163,8 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe public void onPause() { super.onPause(); + LinphoneManager.getInstance().routeAudioToReceiver(); + // Close all opened recordings for (Recording r : recordings) { if (!r.isClosed()) { @@ -148,13 +183,14 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe public void onItemClick(AdapterView parent, View view, int position, long id) { if (recordingAdapter.isEditionEnabled()) { Recording record = recordings.get(position); - recordings.remove(position); if (record.isPlaying()) record.pause(); record.close(); File recordingFile = new File(record.getRecordPath()); - recordingFile.delete(); + if (recordingFile.delete()) { + recordings.remove(record); + } } } @@ -179,13 +215,14 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe int size = recordingAdapter.getSelectedItemCount(); for (int i = 0; i < size; i++) { Recording record = (Recording) objectsToDelete[i]; - recordings.remove(record); if (record.isPlaying()) record.pause(); record.close(); File recordingFile = new File(record.getRecordPath()); - recordingFile.delete(); + if (recordingFile.delete()) { + recordings.remove(record); + } } } } diff --git a/app/src/main/res/layout/recording_cell.xml b/app/src/main/res/layout/recording_cell.xml index f3a477de8..c490c7e5a 100644 --- a/app/src/main/res/layout/recording_cell.xml +++ b/app/src/main/res/layout/recording_cell.xml @@ -131,8 +131,7 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentStart="true" - android:layout_alignParentEnd="true" - android:paddingEnd="0dp"/> + android:layout_alignParentEnd="true"/> From b52e3905cc44017963a09f9ae8f5f545ff0ac9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Thu, 29 Nov 2018 17:42:44 +0100 Subject: [PATCH 08/10] Fix deletion of recordings not working --- .../java/org/linphone/recording/RecordingListFragment.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/org/linphone/recording/RecordingListFragment.java b/app/src/main/java/org/linphone/recording/RecordingListFragment.java index e4b2ddfc6..49c58fd4d 100644 --- a/app/src/main/java/org/linphone/recording/RecordingListFragment.java +++ b/app/src/main/java/org/linphone/recording/RecordingListFragment.java @@ -142,6 +142,9 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe public void onResume() { super.onResume(); + // This is necessary, without it you won't be able to remove recordings as you won't be allowed to. + LinphoneActivity.instance().checkAndRequestExternalStoragePermission(); + LinphoneManager.getInstance().setAudioManagerModeNormal(); LinphoneManager.getInstance().routeAudioToSpeaker(); From 8fb074b9145ac953ccad18f8636f6957d3257f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Fri, 30 Nov 2018 09:30:28 +0100 Subject: [PATCH 09/10] Renamed recordings.xml to recordings_list.xml --- .../main/java/org/linphone/recording/RecordingListFragment.java | 2 +- app/src/main/res/layout/{recordings.xml => recordings_list.xml} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename app/src/main/res/layout/{recordings.xml => recordings_list.xml} (100%) diff --git a/app/src/main/java/org/linphone/recording/RecordingListFragment.java b/app/src/main/java/org/linphone/recording/RecordingListFragment.java index 49c58fd4d..6bf5a4133 100644 --- a/app/src/main/java/org/linphone/recording/RecordingListFragment.java +++ b/app/src/main/java/org/linphone/recording/RecordingListFragment.java @@ -56,7 +56,7 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.recordings, container, false); + View view = inflater.inflate(R.layout.recordings_list, container, false); context = getActivity().getApplicationContext(); selectableHelper = new SelectableHelper(view, this); diff --git a/app/src/main/res/layout/recordings.xml b/app/src/main/res/layout/recordings_list.xml similarity index 100% rename from app/src/main/res/layout/recordings.xml rename to app/src/main/res/layout/recordings_list.xml From 3ede727936d6b3c48681430e5109427b39d1090a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Turnel?= Date: Fri, 30 Nov 2018 09:30:48 +0100 Subject: [PATCH 10/10] Removed useless click listener --- .../org/linphone/recording/RecordingListFragment.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/org/linphone/recording/RecordingListFragment.java b/app/src/main/java/org/linphone/recording/RecordingListFragment.java index 6bf5a4133..040008e80 100644 --- a/app/src/main/java/org/linphone/recording/RecordingListFragment.java +++ b/app/src/main/java/org/linphone/recording/RecordingListFragment.java @@ -35,7 +35,6 @@ import org.linphone.LinphoneActivity; import org.linphone.LinphoneManager; import org.linphone.R; import org.linphone.fragments.FragmentsAvailable; -import org.linphone.mediastream.Log; import org.linphone.utils.FileUtils; import org.linphone.utils.SelectableHelper; @@ -44,7 +43,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class RecordingListFragment extends Fragment implements View.OnClickListener, AdapterView.OnItemClickListener, RecordingViewHolder.ClickListener, SelectableHelper.DeleteListener { +public class RecordingListFragment extends Fragment implements AdapterView.OnItemClickListener, RecordingViewHolder.ClickListener, SelectableHelper.DeleteListener { private RecyclerView recordingList; private List recordings; private TextView noRecordings; @@ -177,11 +176,6 @@ public class RecordingListFragment extends Fragment implements View.OnClickListe } } - @Override - public void onClick(View v) { - - } - @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (recordingAdapter.isEditionEnabled()) {