Merge branch 'feature/call-recorder' into feature/release-4.1
This commit is contained in:
commit
bfafc942e5
26 changed files with 1146 additions and 104 deletions
|
@ -111,6 +111,7 @@ import org.linphone.purchase.InAppPurchaseActivity;
|
||||||
import org.linphone.views.AddressText;
|
import org.linphone.views.AddressText;
|
||||||
import org.linphone.utils.LinphoneGenericActivity;
|
import org.linphone.utils.LinphoneGenericActivity;
|
||||||
import org.linphone.utils.LinphoneUtils;
|
import org.linphone.utils.LinphoneUtils;
|
||||||
|
import org.linphone.recording.RecordingListFragment;
|
||||||
import org.linphone.xmlrpc.XmlRpcHelper;
|
import org.linphone.xmlrpc.XmlRpcHelper;
|
||||||
import org.linphone.xmlrpc.XmlRpcListenerBase;
|
import org.linphone.xmlrpc.XmlRpcListenerBase;
|
||||||
|
|
||||||
|
@ -136,7 +137,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
|
||||||
private StatusFragment statusFragment;
|
private StatusFragment statusFragment;
|
||||||
private TextView missedCalls, missedChats;
|
private TextView missedCalls, missedChats;
|
||||||
private RelativeLayout contacts, history, dialer, chat;
|
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 LinearLayout mTopBar;
|
||||||
private TextView mTopBarTitle;
|
private TextView mTopBarTitle;
|
||||||
private ImageView cancel;
|
private ImageView cancel;
|
||||||
|
@ -433,6 +434,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
|
||||||
case CONTACT_DEVICES:
|
case CONTACT_DEVICES:
|
||||||
fragment = new DevicesFragment();
|
fragment = new DevicesFragment();
|
||||||
break;
|
break;
|
||||||
|
case RECORDING_LIST:
|
||||||
|
fragment = new RecordingListFragment();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -642,6 +646,10 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
|
||||||
changeCurrentFragment(FragmentsAvailable.ABOUT, null);
|
changeCurrentFragment(FragmentsAvailable.ABOUT, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void displayRecordings() {
|
||||||
|
changeCurrentFragment(FragmentsAvailable.RECORDING_LIST, null);
|
||||||
|
}
|
||||||
|
|
||||||
public void displayContactsForEdition(String sipAddress, String displayName) {
|
public void displayContactsForEdition(String sipAddress, String displayName) {
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
extras.putBoolean("EditOnClick", true);
|
extras.putBoolean("EditOnClick", true);
|
||||||
|
@ -1611,6 +1619,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
|
||||||
if (getResources().getBoolean(R.bool.enable_in_app_purchase)) {
|
if (getResources().getBoolean(R.bool.enable_in_app_purchase)) {
|
||||||
sideMenuItems.add(new MenuItem(getResources().getString(R.string.inapp), R.drawable.menu_options));
|
sideMenuItems.add(new MenuItem(getResources().getString(R.string.inapp), R.drawable.menu_options));
|
||||||
}
|
}
|
||||||
|
sideMenuItems.add(new MenuItem(getResources().getString(R.string.menu_recordings), R.drawable.menu_recordings));
|
||||||
sideMenuItems.add(new MenuItem(getResources().getString(R.string.menu_about), R.drawable.menu_about));
|
sideMenuItems.add(new MenuItem(getResources().getString(R.string.menu_about), R.drawable.menu_about));
|
||||||
sideMenuContent = findViewById(R.id.side_menu_content);
|
sideMenuContent = findViewById(R.id.side_menu_content);
|
||||||
sideMenuItemList = findViewById(R.id.item_list);
|
sideMenuItemList = findViewById(R.id.item_list);
|
||||||
|
@ -1634,6 +1643,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
|
||||||
LinphoneActivity.instance().displayInapp();
|
LinphoneActivity.instance().displayInapp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (sideMenuItemList.getAdapter().getItem(i).toString().equals(getString(R.string.menu_recordings))) {
|
||||||
|
LinphoneActivity.instance().displayRecordings();
|
||||||
|
}
|
||||||
openOrCloseSideMenu(false);
|
openOrCloseSideMenu(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -779,7 +779,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou
|
||||||
public void setHandsetMode(Boolean on) {
|
public void setHandsetMode(Boolean on) {
|
||||||
if (mLc.isIncomingInvitePending() && on) {
|
if (mLc.isIncomingInvitePending() && on) {
|
||||||
handsetON = true;
|
handsetON = true;
|
||||||
mLc.acceptCall(mLc.getCurrentCall());
|
acceptCall(mLc.getCurrentCall());
|
||||||
LinphoneActivity.instance().startIncallActivity();
|
LinphoneActivity.instance().startIncallActivity();
|
||||||
} else if (on && CallActivity.isInstanciated()) {
|
} else if (on && CallActivity.isInstanciated()) {
|
||||||
handsetON = true;
|
handsetON = true;
|
||||||
|
@ -1148,6 +1148,10 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAudioManagerModeNormal() {
|
||||||
|
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
public void setAudioManagerInCallMode() {
|
public void setAudioManagerInCallMode() {
|
||||||
if (mAudioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
|
if (mAudioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
|
||||||
Log.w("[AudioManager] already in MODE_IN_COMMUNICATION, skipping...");
|
Log.w("[AudioManager] already in MODE_IN_COMMUNICATION, skipping...");
|
||||||
|
@ -1179,7 +1183,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou
|
||||||
public void run() {
|
public void run() {
|
||||||
if (mLc != null) {
|
if (mLc != null) {
|
||||||
if (mLc.getCallsNb() > 0) {
|
if (mLc.getCallsNb() > 0) {
|
||||||
mLc.acceptCall(call);
|
acceptCall(call);
|
||||||
if (LinphoneManager.getInstance() != null) {
|
if (LinphoneManager.getInstance() != null) {
|
||||||
LinphoneManager.getInstance().routeAudioToReceiver();
|
LinphoneManager.getInstance().routeAudioToReceiver();
|
||||||
if (LinphoneActivity.instance() != null)
|
if (LinphoneActivity.instance() != null)
|
||||||
|
@ -1471,14 +1475,24 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou
|
||||||
return reinviteWithVideo();
|
return reinviteWithVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean acceptCallIfIncomingPending() throws CoreException {
|
public boolean acceptCall(Call call) {
|
||||||
if (mLc.isIncomingInvitePending()) {
|
if (call == null) return false;
|
||||||
mLc.acceptCall(mLc.getCurrentCall());
|
|
||||||
return true;
|
CallParams params = LinphoneManager.getLc().createCallParams(call);
|
||||||
}
|
|
||||||
|
boolean isLowBandwidthConnection = !LinphoneUtils.isHighBandwidthConnection(LinphoneService.instance().getApplicationContext());
|
||||||
|
|
||||||
|
if (params != null) {
|
||||||
|
params.enableLowBandwidth(isLowBandwidthConnection);
|
||||||
|
params.setRecordFile(FileUtils.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) {
|
public boolean acceptCallWithParams(Call call, CallParams params) {
|
||||||
mLc.acceptCallWithParams(call, params);
|
mLc.acceptCallWithParams(call, params);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -101,6 +101,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
private static final int PERMISSIONS_REQUEST_CAMERA = 202;
|
private static final int PERMISSIONS_REQUEST_CAMERA = 202;
|
||||||
private static final int PERMISSIONS_ENABLED_CAMERA = 203;
|
private static final int PERMISSIONS_ENABLED_CAMERA = 203;
|
||||||
private static final int PERMISSIONS_ENABLED_MIC = 204;
|
private static final int PERMISSIONS_ENABLED_MIC = 204;
|
||||||
|
private static final int PERMISSIONS_EXTERNAL_STORAGE = 205;
|
||||||
|
|
||||||
private static CallActivity instance;
|
private static CallActivity instance;
|
||||||
|
|
||||||
|
@ -109,14 +110,14 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
private ImageView switchCamera;
|
private ImageView switchCamera;
|
||||||
private TextView missedChats;
|
private TextView missedChats;
|
||||||
private RelativeLayout mActiveCallHeader, sideMenuContent, avatar_layout;
|
private RelativeLayout mActiveCallHeader, sideMenuContent, avatar_layout;
|
||||||
private ImageView pause, hangUp, dialer, video, micro, speaker, options, addCall, transfer, conference, conferenceStatus, contactPicture;
|
private ImageView pause, hangUp, dialer, video, micro, speaker, options, addCall, transfer, conference, conferenceStatus, contactPicture, recordCall, recording;
|
||||||
private ImageView audioRoute, routeSpeaker, routeEarpiece, routeBluetooth, menu, chat;
|
private ImageView audioRoute, routeSpeaker, routeEarpiece, routeBluetooth, menu, chat;
|
||||||
private LinearLayout mNoCurrentCall, callInfo, mCallPaused;
|
private LinearLayout mNoCurrentCall, callInfo, mCallPaused;
|
||||||
private ProgressBar videoProgress;
|
private ProgressBar videoProgress;
|
||||||
private StatusFragment status;
|
private StatusFragment status;
|
||||||
private CallAudioFragment audioCallFragment;
|
private CallAudioFragment audioCallFragment;
|
||||||
private CallVideoFragment videoCallFragment;
|
private CallVideoFragment videoCallFragment;
|
||||||
private boolean isSpeakerEnabled = false, isMicMuted = false, isTransferAllowed, isVideoAsk;
|
private boolean isSpeakerEnabled = false, isMicMuted = false, isTransferAllowed, isVideoAsk, isRecording = false;
|
||||||
private LinearLayout mControlsLayout;
|
private LinearLayout mControlsLayout;
|
||||||
private Numpad numpad;
|
private Numpad numpad;
|
||||||
private int cameraNumber;
|
private int cameraNumber;
|
||||||
|
@ -194,7 +195,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
return;
|
return;
|
||||||
} else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) {
|
} else if (state == State.Paused || state == State.PausedByRemote || state == State.Pausing) {
|
||||||
if (LinphoneManager.getLc().getCurrentCall() != null) {
|
if (LinphoneManager.getLc().getCurrentCall() != null) {
|
||||||
enabledVideoButton(false);
|
video.setEnabled(false);
|
||||||
}
|
}
|
||||||
if (isVideoEnabled(call)) {
|
if (isVideoEnabled(call)) {
|
||||||
showAudioView();
|
showAudioView();
|
||||||
|
@ -207,7 +208,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (LinphoneManager.getLc().getCurrentCall() != null) {
|
if (LinphoneManager.getLc().getCurrentCall() != null) {
|
||||||
enabledVideoButton(true);
|
video.setEnabled(true);
|
||||||
}
|
}
|
||||||
} else if (state == State.StreamsRunning) {
|
} else if (state == State.StreamsRunning) {
|
||||||
switchVideo(isVideoEnabled(call));
|
switchVideo(isVideoEnabled(call));
|
||||||
|
@ -344,7 +345,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
//TopBar
|
//TopBar
|
||||||
video = findViewById(R.id.video);
|
video = findViewById(R.id.video);
|
||||||
video.setOnClickListener(this);
|
video.setOnClickListener(this);
|
||||||
enabledVideoButton(false);
|
video.setEnabled(false);
|
||||||
|
|
||||||
videoProgress = findViewById(R.id.video_in_progress);
|
videoProgress = findViewById(R.id.video_in_progress);
|
||||||
videoProgress.setVisibility(View.GONE);
|
videoProgress.setVisibility(View.GONE);
|
||||||
|
@ -380,7 +381,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
|
|
||||||
pause = findViewById(R.id.pause);
|
pause = findViewById(R.id.pause);
|
||||||
pause.setOnClickListener(this);
|
pause.setOnClickListener(this);
|
||||||
enabledPauseButton(false);
|
pause.setEnabled(false);
|
||||||
|
|
||||||
mActiveCallHeader = findViewById(R.id.active_call);
|
mActiveCallHeader = findViewById(R.id.active_call);
|
||||||
mNoCurrentCall = findViewById(R.id.no_current_call);
|
mNoCurrentCall = findViewById(R.id.no_current_call);
|
||||||
|
@ -402,6 +403,15 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
conference.setEnabled(false);
|
conference.setEnabled(false);
|
||||||
conference.setOnClickListener(this);
|
conference.setOnClickListener(this);
|
||||||
|
|
||||||
|
recordCall = findViewById(R.id.record_call);
|
||||||
|
recordCall.setOnClickListener(this);
|
||||||
|
recordCall.setEnabled(false);
|
||||||
|
|
||||||
|
recording = findViewById(R.id.recording);
|
||||||
|
recording.setOnClickListener(this);
|
||||||
|
recording.setEnabled(false);
|
||||||
|
recording.setVisibility(View.GONE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
audioRoute = findViewById(R.id.audio_route);
|
audioRoute = findViewById(R.id.audio_route);
|
||||||
audioRoute.setOnClickListener(this);
|
audioRoute.setOnClickListener(this);
|
||||||
|
@ -489,6 +499,15 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case PERMISSIONS_EXTERNAL_STORAGE:
|
||||||
|
LinphoneUtils.dispatchOnUIThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
toggleCallRecording(!isRecording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,7 +547,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
|
|
||||||
public void refreshInCallActions() {
|
public void refreshInCallActions() {
|
||||||
if (!LinphonePreferences.instance().isVideoEnabled() || isConferenceRunning) {
|
if (!LinphonePreferences.instance().isVideoEnabled() || isConferenceRunning) {
|
||||||
enabledVideoButton(false);
|
video.setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
if (video.isEnabled()) {
|
if (video.isEnabled()) {
|
||||||
if (isVideoEnabled(LinphoneManager.getLc().getCurrentCall())) {
|
if (isVideoEnabled(LinphoneManager.getLc().getCurrentCall())) {
|
||||||
|
@ -582,33 +601,27 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
}
|
}
|
||||||
|
|
||||||
//Enabled transfer button
|
//Enabled transfer button
|
||||||
if (isTransferAllowed && !LinphoneManager.getLc().soundResourcesLocked())
|
transfer.setEnabled(isTransferAllowed && !LinphoneManager.getLc().soundResourcesLocked());
|
||||||
enabledTransferButton(true);
|
|
||||||
|
|
||||||
//Enable conference button
|
//Enable conference button
|
||||||
if (LinphoneManager.getLc().getCallsNb() > 1 && LinphoneManager.getLc().getCallsNb() > confsize && !LinphoneManager.getLc().soundResourcesLocked()) {
|
conference.setEnabled(LinphoneManager.getLc().getCallsNb() > 1 && LinphoneManager.getLc().getCallsNb() > confsize && !LinphoneManager.getLc().soundResourcesLocked());
|
||||||
enabledConferenceButton(true);
|
|
||||||
} else {
|
|
||||||
enabledConferenceButton(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
addCall.setEnabled(LinphoneManager.getLc().getCallsNb() < LinphoneManager.getLc().getMaxCalls() && !LinphoneManager.getLc().soundResourcesLocked());
|
addCall.setEnabled(LinphoneManager.getLc().getCallsNb() < LinphoneManager.getLc().getMaxCalls() && !LinphoneManager.getLc().soundResourcesLocked());
|
||||||
options.setEnabled(!getResources().getBoolean(R.bool.disable_options_in_call) && (addCall.isEnabled() || transfer.isEnabled()));
|
options.setEnabled(!getResources().getBoolean(R.bool.disable_options_in_call) && (addCall.isEnabled() || transfer.isEnabled()));
|
||||||
|
|
||||||
if (LinphoneManager.getLc().getCurrentCall() != null && LinphonePreferences.instance().isVideoEnabled() && !LinphoneManager.getLc().getCurrentCall().mediaInProgress()) {
|
recordCall.setEnabled(!LinphoneManager.getLc().soundResourcesLocked());
|
||||||
enabledVideoButton(true);
|
recordCall.setImageResource(isRecording ? R.drawable.options_rec_selected : R.drawable.options_rec_default);
|
||||||
} else {
|
|
||||||
enabledVideoButton(false);
|
recording.setEnabled(isRecording);
|
||||||
}
|
recording.setVisibility(isRecording ? View.VISIBLE : View.GONE);
|
||||||
if (LinphoneManager.getLc().getCurrentCall() != null && !LinphoneManager.getLc().getCurrentCall().mediaInProgress()) {
|
|
||||||
enabledPauseButton(true);
|
|
||||||
} else {
|
video.setEnabled(LinphoneManager.getLc().getCurrentCall() != null && LinphonePreferences.instance().isVideoEnabled() && !LinphoneManager.getLc().getCurrentCall().mediaInProgress());
|
||||||
enabledPauseButton(false);
|
|
||||||
}
|
pause.setEnabled(LinphoneManager.getLc().getCurrentCall() != null && !LinphoneManager.getLc().getCurrentCall().mediaInProgress());
|
||||||
|
|
||||||
micro.setEnabled(true);
|
micro.setEnabled(true);
|
||||||
if (!isTablet()) {
|
speaker.setEnabled(!isTablet());
|
||||||
speaker.setEnabled(true);
|
|
||||||
}
|
|
||||||
transfer.setEnabled(true);
|
transfer.setEnabled(true);
|
||||||
pause.setEnabled(true);
|
pause.setEnabled(true);
|
||||||
dialer.setEnabled(true);
|
dialer.setEnabled(true);
|
||||||
|
@ -649,6 +662,17 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
toggleSpeaker();
|
toggleSpeaker();
|
||||||
} else if (id == R.id.add_call) {
|
} else if (id == R.id.add_call) {
|
||||||
goBackToDialer();
|
goBackToDialer();
|
||||||
|
} else if (id == R.id.record_call) {
|
||||||
|
int externalStorage = getPackageManager().checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, getPackageName());
|
||||||
|
Log.i("[Permission] External storage permission is " + (externalStorage == PackageManager.PERMISSION_GRANTED ? "granted" : "denied"));
|
||||||
|
|
||||||
|
if (externalStorage == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
toggleCallRecording(!isRecording);
|
||||||
|
} else {
|
||||||
|
checkAndRequestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, PERMISSIONS_EXTERNAL_STORAGE);
|
||||||
|
}
|
||||||
|
} else if (id == R.id.recording) {
|
||||||
|
toggleCallRecording(false);
|
||||||
} else if (id == R.id.pause) {
|
} else if (id == R.id.pause) {
|
||||||
pauseOrResumeCall(LinphoneManager.getLc().getCurrentCall());
|
pauseOrResumeCall(LinphoneManager.getLc().getCurrentCall());
|
||||||
} else if (id == R.id.hang_up) {
|
} else if (id == R.id.hang_up) {
|
||||||
|
@ -700,35 +724,31 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enabledVideoButton(boolean enabled) {
|
private void toggleCallRecording(boolean enable) {
|
||||||
if (enabled) {
|
Call call = LinphoneManager.getLc().getCurrentCall();
|
||||||
video.setEnabled(true);
|
|
||||||
} else {
|
|
||||||
video.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enabledPauseButton(boolean enabled) {
|
if (call == null) return;
|
||||||
if (enabled) {
|
|
||||||
pause.setEnabled(true);
|
|
||||||
} else {
|
|
||||||
pause.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enabledTransferButton(boolean enabled) {
|
if (enable && !isRecording) {
|
||||||
if (enabled) {
|
call.startRecording();
|
||||||
transfer.setEnabled(true);
|
Log.d("start call recording");
|
||||||
} else {
|
|
||||||
transfer.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enabledConferenceButton(boolean enabled) {
|
recordCall.setImageResource(R.drawable.options_rec_selected);
|
||||||
if (enabled) {
|
|
||||||
conference.setEnabled(true);
|
recording.setVisibility(View.VISIBLE);
|
||||||
} else {
|
recording.setEnabled(true);
|
||||||
conference.setEnabled(false);
|
|
||||||
|
isRecording = true;
|
||||||
|
} else if (!enable && isRecording) {
|
||||||
|
call.stopRecording();
|
||||||
|
Log.d("stop call recording");
|
||||||
|
|
||||||
|
recordCall.setImageResource(R.drawable.options_rec_default);
|
||||||
|
|
||||||
|
recording.setVisibility(View.GONE);
|
||||||
|
recording.setEnabled(false);
|
||||||
|
|
||||||
|
isRecording = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,6 +930,10 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
Core lc = LinphoneManager.getLc();
|
Core lc = LinphoneManager.getLc();
|
||||||
Call currentCall = lc.getCurrentCall();
|
Call currentCall = lc.getCurrentCall();
|
||||||
|
|
||||||
|
if (isRecording) {
|
||||||
|
toggleCallRecording(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (currentCall != null) {
|
if (currentCall != null) {
|
||||||
lc.terminateCall(currentCall);
|
lc.terminateCall(currentCall);
|
||||||
} else if (lc.isInConference()) {
|
} else if (lc.isInConference()) {
|
||||||
|
@ -963,6 +987,7 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
transfer.setVisibility(View.INVISIBLE);
|
transfer.setVisibility(View.INVISIBLE);
|
||||||
addCall.setVisibility(View.INVISIBLE);
|
addCall.setVisibility(View.INVISIBLE);
|
||||||
conference.setVisibility(View.INVISIBLE);
|
conference.setVisibility(View.INVISIBLE);
|
||||||
|
recordCall.setVisibility(View.INVISIBLE);
|
||||||
displayVideoCall(false);
|
displayVideoCall(false);
|
||||||
numpad.setVisibility(View.GONE);
|
numpad.setVisibility(View.GONE);
|
||||||
options.setSelected(false);
|
options.setSelected(false);
|
||||||
|
@ -1023,12 +1048,14 @@ public class CallActivity extends LinphoneGenericActivity implements OnClickList
|
||||||
}
|
}
|
||||||
addCall.setVisibility(View.INVISIBLE);
|
addCall.setVisibility(View.INVISIBLE);
|
||||||
conference.setVisibility(View.INVISIBLE);
|
conference.setVisibility(View.INVISIBLE);
|
||||||
|
recordCall.setVisibility(View.INVISIBLE);
|
||||||
} else { //Display options
|
} else { //Display options
|
||||||
if (isTransferAllowed) {
|
if (isTransferAllowed) {
|
||||||
transfer.setVisibility(View.VISIBLE);
|
transfer.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
addCall.setVisibility(View.VISIBLE);
|
addCall.setVisibility(View.VISIBLE);
|
||||||
conference.setVisibility(View.VISIBLE);
|
conference.setVisibility(View.VISIBLE);
|
||||||
|
recordCall.setVisibility(View.VISIBLE);
|
||||||
options.setSelected(true);
|
options.setSelected(true);
|
||||||
transfer.setEnabled(LinphoneManager.getLc().getCurrentCall() != null);
|
transfer.setEnabled(LinphoneManager.getLc().getCurrentCall() != null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,17 +245,7 @@ public class CallIncomingActivity extends LinphoneGenericActivity {
|
||||||
}
|
}
|
||||||
alreadyAcceptedOrDeniedCall = true;
|
alreadyAcceptedOrDeniedCall = true;
|
||||||
|
|
||||||
CallParams params = LinphoneManager.getLc().createCallParams(mCall);
|
if (!LinphoneManager.getInstance().acceptCall(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)) {
|
|
||||||
// the above method takes care of Samsung Galaxy S
|
// the above method takes care of Samsung Galaxy S
|
||||||
Toast.makeText(this, R.string.couldnt_accept_call, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.couldnt_accept_call, Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.linphone.LinphoneManager;
|
import org.linphone.LinphoneManager;
|
||||||
|
import org.linphone.utils.FileUtils;
|
||||||
import org.linphone.core.Address;
|
import org.linphone.core.Address;
|
||||||
import org.linphone.core.Call;
|
import org.linphone.core.Call;
|
||||||
import org.linphone.core.CallParams;
|
import org.linphone.core.CallParams;
|
||||||
|
@ -63,6 +64,9 @@ public class CallManager {
|
||||||
Log.d("Low bandwidth enabled in call params");
|
Log.d("Low bandwidth enabled in call params");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String recordFile = FileUtils.getCallRecordingFilename(LinphoneManager.getInstance().getContext(), lAddress);
|
||||||
|
params.setRecordFile(recordFile);
|
||||||
|
|
||||||
lc.inviteAddressWithParams(lAddress, params);
|
lc.inviteAddressWithParams(lAddress, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,8 @@ public enum FragmentsAvailable {
|
||||||
INFO_GROUP_CHAT,
|
INFO_GROUP_CHAT,
|
||||||
GROUP_CHAT,
|
GROUP_CHAT,
|
||||||
MESSAGE_IMDN,
|
MESSAGE_IMDN,
|
||||||
CONTACT_DEVICES;
|
CONTACT_DEVICES,
|
||||||
|
RECORDING_LIST;
|
||||||
|
|
||||||
public boolean shouldAddItselfToTheRightOf(FragmentsAvailable fragment) {
|
public boolean shouldAddItselfToTheRightOf(FragmentsAvailable fragment) {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.linphone.core.ChatRoomListenerStub;
|
||||||
import org.linphone.core.Core;
|
import org.linphone.core.Core;
|
||||||
import org.linphone.core.Factory;
|
import org.linphone.core.Factory;
|
||||||
import org.linphone.core.ProxyConfig;
|
import org.linphone.core.ProxyConfig;
|
||||||
|
import org.linphone.fragments.FragmentsAvailable;
|
||||||
import org.linphone.mediastream.Log;
|
import org.linphone.mediastream.Log;
|
||||||
|
|
||||||
public class HistoryDetailFragment extends Fragment implements OnClickListener {
|
public class HistoryDetailFragment extends Fragment implements OnClickListener {
|
||||||
|
|
|
@ -45,6 +45,7 @@ import org.linphone.core.Call;
|
||||||
import org.linphone.core.CallLog;
|
import org.linphone.core.CallLog;
|
||||||
import org.linphone.utils.SelectableHelper;
|
import org.linphone.utils.SelectableHelper;
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
161
app/src/main/java/org/linphone/recording/Recording.java
Normal file
161
app/src/main/java/org/linphone/recording/Recording.java
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
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.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
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.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Recording implements PlayerListener, Comparable<Recording> {
|
||||||
|
private String recordPath, name;
|
||||||
|
private Date recordDate;
|
||||||
|
private Player player;
|
||||||
|
private RecordingListener listener;
|
||||||
|
private Handler handler;
|
||||||
|
private Runnable updateCurrentPositionTimer;
|
||||||
|
|
||||||
|
public static final Pattern RECORD_PATTERN = Pattern.compile(".*/(.*)_(\\d{2}-\\d{2}-\\d{4}-\\d{2}-\\d{2}-\\d{2})\\..*");
|
||||||
|
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
|
public Recording(Context context, String recordPath) {
|
||||||
|
this.recordPath = recordPath;
|
||||||
|
|
||||||
|
Matcher m = RECORD_PATTERN.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
if (isClosed()) {
|
||||||
|
player.open(recordPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.start();
|
||||||
|
handler.post(updateCurrentPositionTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlaying() {
|
||||||
|
return player.getState() == Player.State.Playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
if (!isClosed()) {
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPaused() {
|
||||||
|
return player.getState() == Player.State.Paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void seek(int i) {
|
||||||
|
if (!isClosed()) player.seek(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentPosition() {
|
||||||
|
if (isClosed()) {
|
||||||
|
player.open(recordPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return player.getCurrentPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDuration() {
|
||||||
|
if (isClosed()) {
|
||||||
|
player.open(recordPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
219
app/src/main/java/org/linphone/recording/RecordingAdapter.java
Normal file
219
app/src/main/java/org/linphone/recording/RecordingAdapter.java
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
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.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
|
||||||
|
import org.linphone.R;
|
||||||
|
import org.linphone.utils.SelectableAdapter;
|
||||||
|
import org.linphone.utils.SelectableHelper;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class RecordingAdapter extends SelectableAdapter<RecordingViewHolder> {
|
||||||
|
private List<Recording> recordings;
|
||||||
|
private Context context;
|
||||||
|
private RecordingViewHolder.ClickListener clickListener;
|
||||||
|
|
||||||
|
public RecordingAdapter(Context context, List<Recording> recordings, RecordingViewHolder.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 RecordingViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
|
||||||
|
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recording_cell, viewGroup, false);
|
||||||
|
return new RecordingViewHolder(v, clickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull final RecordingViewHolder 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))
|
||||||
|
));
|
||||||
|
|
||||||
|
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(record.getDuration());
|
||||||
|
viewHolder.progressionBar.setProgress(0);
|
||||||
|
viewHolder.progressionBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
if (fromUser) {
|
||||||
|
int progressToSet = progress > 0 && progress < seekBar.getMax() ? progress : 0;
|
||||||
|
|
||||||
|
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))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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:%02d",
|
||||||
|
TimeUnit.MILLISECONDS.toMinutes(currentPosition),
|
||||||
|
TimeUnit.MILLISECONDS.toSeconds(currentPosition) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(currentPosition))
|
||||||
|
));
|
||||||
|
viewHolder.progressionBar.setProgress(currentPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endOfRecordReached() {
|
||||||
|
record.pause();
|
||||||
|
record.seek(0);
|
||||||
|
viewHolder.progressionBar.setProgress(0);
|
||||||
|
viewHolder.currentPosition.setText("00:00");
|
||||||
|
viewHolder.playButton.setImageResource(R.drawable.record_play);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
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.LinphoneActivity;
|
||||||
|
import org.linphone.LinphoneManager;
|
||||||
|
import org.linphone.R;
|
||||||
|
import org.linphone.fragments.FragmentsAvailable;
|
||||||
|
import org.linphone.utils.FileUtils;
|
||||||
|
import org.linphone.utils.SelectableHelper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RecordingListFragment extends Fragment implements AdapterView.OnItemClickListener, RecordingViewHolder.ClickListener, SelectableHelper.DeleteListener {
|
||||||
|
private RecyclerView recordingList;
|
||||||
|
private List<Recording> 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_list, 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 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);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (Recording.RECORD_PATTERN.matcher(f.getPath()).matches()) {
|
||||||
|
recordings.add(new Recording(context, f.getPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(recordings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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();
|
||||||
|
|
||||||
|
if (LinphoneActivity.isInstanciated()) {
|
||||||
|
LinphoneActivity.instance().selectMenu(FragmentsAvailable.RECORDING_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDeletedRecordings();
|
||||||
|
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();
|
||||||
|
|
||||||
|
LinphoneManager.getInstance().routeAudioToReceiver();
|
||||||
|
|
||||||
|
// Close all opened recordings
|
||||||
|
for (Recording r : recordings) {
|
||||||
|
if (!r.isClosed()) {
|
||||||
|
if (r.isPlaying()) r.pause();
|
||||||
|
r.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
if (recordingAdapter.isEditionEnabled()) {
|
||||||
|
Recording record = recordings.get(position);
|
||||||
|
|
||||||
|
if (record.isPlaying()) record.pause();
|
||||||
|
record.close();
|
||||||
|
|
||||||
|
File recordingFile = new File(record.getRecordPath());
|
||||||
|
if (recordingFile.delete()) {
|
||||||
|
recordings.remove(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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];
|
||||||
|
|
||||||
|
if (record.isPlaying()) record.pause();
|
||||||
|
record.close();
|
||||||
|
|
||||||
|
File recordingFile = new File(record.getRecordPath());
|
||||||
|
if (recordingFile.delete()) {
|
||||||
|
recordings.remove(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -28,6 +29,7 @@ import android.provider.OpenableColumns;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.linphone.LinphoneManager;
|
import org.linphone.LinphoneManager;
|
||||||
|
import org.linphone.core.Address;
|
||||||
import org.linphone.core.ChatMessage;
|
import org.linphone.core.ChatMessage;
|
||||||
import org.linphone.core.Content;
|
import org.linphone.core.Content;
|
||||||
import org.linphone.core.Friend;
|
import org.linphone.core.Friend;
|
||||||
|
@ -40,6 +42,7 @@ import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -197,6 +200,30 @@ public class FileUtils {
|
||||||
return storageDir;
|
return storageDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getRecordingsDirectory(Context mContext) {
|
||||||
|
String recordingsDir = Environment.getExternalStorageDirectory() + "/" + mContext.getString(mContext.getResources().getIdentifier("app_name", "string", mContext.getPackageName())) + "/recordings";
|
||||||
|
File file = new File(recordingsDir);
|
||||||
|
if (!file.isDirectory() || !file.exists()) {
|
||||||
|
Log.w("Directory " + file + " doesn't seem to exists yet, let's create it");
|
||||||
|
file.mkdirs();
|
||||||
|
LinphoneManager.getInstance().getMediaScanner().scanFile(file);
|
||||||
|
}
|
||||||
|
return recordingsDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SimpleDateFormat")
|
||||||
|
public static String getCallRecordingFilename(Context context, Address address) {
|
||||||
|
String fileName = getRecordingsDirectory(context) + "/";
|
||||||
|
|
||||||
|
String name = address.getDisplayName() == null ? address.getUsername() : address.getDisplayName();
|
||||||
|
fileName += name + "_";
|
||||||
|
|
||||||
|
DateFormat format = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss");
|
||||||
|
fileName += format.format(new Date()) + ".mkv";
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
public static void scanFile(ChatMessage message) {
|
public static void scanFile(ChatMessage message) {
|
||||||
String appData = message.getAppdata();
|
String appData = message.getAppdata();
|
||||||
if (appData == null) {
|
if (appData == null) {
|
||||||
|
|
|
@ -58,8 +58,6 @@ public class CallButton extends ImageView implements OnClickListener, AddressAwa
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
try {
|
|
||||||
if (!LinphoneManager.getInstance().acceptCallIfIncomingPending()) {
|
|
||||||
if (mAddress.getText().length() > 0) {
|
if (mAddress.getText().length() > 0) {
|
||||||
LinphoneManager.getInstance().newOutgoingCall(mAddress);
|
LinphoneManager.getInstance().newOutgoingCall(mAddress);
|
||||||
} else {
|
} else {
|
||||||
|
@ -87,11 +85,6 @@ public class CallButton extends ImageView implements OnClickListener, AddressAwa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (CoreException e) {
|
|
||||||
LinphoneManager.getInstance().terminateCall();
|
|
||||||
onWrongDestinationAddress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onWrongDestinationAddress() {
|
protected void onWrongDestinationAddress() {
|
||||||
Toast.makeText(getContext()
|
Toast.makeText(getContext()
|
||||||
|
|
BIN
app/src/main/res/drawable-xhdpi/menu_recordings.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/menu_recordings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/drawable-xhdpi/options_rec_default.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/options_rec_default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/drawable-xhdpi/options_rec_selected.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/options_rec_selected.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/res/drawable-xhdpi/record_pause.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/record_pause.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
BIN
app/src/main/res/drawable-xhdpi/record_play.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/record_play.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
BIN
app/src/main/res/drawable-xhdpi/recording.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/recording.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
|
@ -181,6 +181,19 @@
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/recording"
|
||||||
|
android:src="@drawable/recording"
|
||||||
|
android:background="@drawable/round_orange_button_background"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:contentDescription="@string/content_description_record_call"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -369,10 +382,21 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp" />
|
android:layout_height="60dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/record_call"
|
||||||
|
android:src="@drawable/options_rec_default"
|
||||||
|
android:background="@drawable/button_background"
|
||||||
|
android:contentDescription="@string/content_description_record_call"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:padding="15dp"
|
||||||
|
android:layout_above="@id/options"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/add_call"
|
android:id="@+id/add_call"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:layout_above="@id/options"
|
android:layout_above="@id/record_call"
|
||||||
android:src="@drawable/options_add_call"
|
android:src="@drawable/options_add_call"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/button_background"
|
||||||
android:contentDescription="@string/content_description_add_call"
|
android:contentDescription="@string/content_description_add_call"
|
||||||
|
|
|
@ -181,6 +181,19 @@
|
||||||
android:layout_gravity="center"/>
|
android:layout_gravity="center"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/recording"
|
||||||
|
android:src="@drawable/recording"
|
||||||
|
android:background="@drawable/round_orange_button_background"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:contentDescription="@string/content_description_record_call"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -310,10 +323,21 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp" />
|
android:layout_height="60dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/record_call"
|
||||||
|
android:src="@drawable/options_rec_default"
|
||||||
|
android:background="@drawable/button_background"
|
||||||
|
android:contentDescription="@string/content_description_record_call"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:padding="15dp"
|
||||||
|
android:layout_above="@id/options"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/add_call"
|
android:id="@+id/add_call"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:layout_above="@id/options"
|
android:layout_above="@id/record_call"
|
||||||
android:src="@drawable/options_add_call"
|
android:src="@drawable/options_add_call"
|
||||||
android:background="@drawable/button_background"
|
android:background="@drawable/button_background"
|
||||||
android:contentDescription="@string/content_description_add_call"
|
android:contentDescription="@string/content_description_add_call"
|
||||||
|
|
140
app/src/main/res/layout/recording_cell.xml
Normal file
140
app/src/main/res/layout/recording_cell.xml
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@color/colorH"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/separator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/separator_text"
|
||||||
|
style="@style/font1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:background="@color/colorE"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/record_play"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:gravity="center"/>
|
||||||
|
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/delete"
|
||||||
|
android:button="@drawable/checkbox"
|
||||||
|
android:contentDescription="@string/content_description_delete"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:clickable="false"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
android:paddingRight="5dp" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toRightOf="@id/record_play"
|
||||||
|
android:layout_toLeftOf="@id/delete"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginLeft="10dp"
|
||||||
|
android:layout_marginRight="10dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/record_description"
|
||||||
|
style="@style/font6"
|
||||||
|
android:lines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_alignParentStart="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/record_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text=" - "
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/record_date"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/record_time"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
style="@style/font6"
|
||||||
|
android:lines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentEnd="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/record_current_time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="/"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/record_duration"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/record_progression_bar"
|
||||||
|
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||||
|
android:progressTint="@color/colorA"
|
||||||
|
android:thumbTint="@color/colorA"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentEnd="true"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
67
app/src/main/res/layout/recordings_list.xml
Normal file
67
app/src/main/res/layout/recordings_list.xml
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:background="@color/colorH"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/top_bar"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:background="@color/colorF"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="0.8"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/edit"
|
||||||
|
android:src="@drawable/delete"
|
||||||
|
android:background="@drawable/toolbar_button"
|
||||||
|
android:contentDescription="@string/content_description_edit_list"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="0.2"
|
||||||
|
android:padding="15dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/edit_list"/>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/recording_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:divider="@color/colorE"
|
||||||
|
android:cacheColorHint="@color/transparent"
|
||||||
|
android:dividerHeight="1dp"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/recording_fetch_in_progress"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_recordings"
|
||||||
|
android:text="@string/no_recordings"
|
||||||
|
style="@style/font6"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_centerVertical="true"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -252,6 +252,7 @@
|
||||||
<!-- Side Menu -->
|
<!-- Side Menu -->
|
||||||
<string name="menu_assistant">Assistant</string>
|
<string name="menu_assistant">Assistant</string>
|
||||||
<string name="menu_settings">Settings</string>
|
<string name="menu_settings">Settings</string>
|
||||||
|
<string name="menu_recordings">My recordings</string>
|
||||||
<string name="menu_about">About</string>
|
<string name="menu_about">About</string>
|
||||||
<string name="quit">Quit</string>
|
<string name="quit">Quit</string>
|
||||||
|
|
||||||
|
@ -288,6 +289,10 @@
|
||||||
<string name="call_stats_display_filter">Display filter:</string>
|
<string name="call_stats_display_filter">Display filter:</string>
|
||||||
<string name="call">Call</string>
|
<string name="call">Call</string>
|
||||||
|
|
||||||
|
<!-- Recordings -->
|
||||||
|
<string name="no_recordings">No recordings.</string>
|
||||||
|
<string name="recordings_delete_dialog">Do you really want to delete and leave the selected recordings?</string>
|
||||||
|
|
||||||
<!-- About -->
|
<!-- About -->
|
||||||
<string name="menu_send_log">Send log</string>
|
<string name="menu_send_log">Send log</string>
|
||||||
<string name="menu_reset_log">Reset log</string>
|
<string name="menu_reset_log">Reset log</string>
|
||||||
|
@ -558,4 +563,5 @@
|
||||||
<string name="content_title_notification">Linphone instant messages notifications</string>
|
<string name="content_title_notification">Linphone instant messages notifications</string>
|
||||||
<string name="content_description_conversation_subject">Group chat room subject</string>
|
<string name="content_description_conversation_subject">Group chat room subject</string>
|
||||||
<string name="content_description_conversation_infos">Group chat room info</string>
|
<string name="content_description_conversation_infos">Group chat room info</string>
|
||||||
|
<string name="content_description_record_call">Record call</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue