summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoremancebo <emancebo@cyngn.com>2014-08-04 12:03:34 -0700
committeremancebo <emancebo@cyngn.com>2015-02-17 14:27:09 -0800
commitbba9d00eb775232678eae83a0b588b28955049e7 (patch)
treee0c0aa9d802b4b13689f812ff53339e475a22224
parentacad21a57c6bdf2a7aef13a1862157cf9bc3b96c (diff)
downloadpackages_apps_InCallUI-bba9d00eb775232678eae83a0b588b28955049e7.tar.gz
packages_apps_InCallUI-bba9d00eb775232678eae83a0b588b28955049e7.tar.bz2
packages_apps_InCallUI-bba9d00eb775232678eae83a0b588b28955049e7.zip
Add an option in the InCall UI to perform call recording
Use existing menu items in overflow menu to start/stop recording instead of adding buttons in the call button fragment Change-Id: Icc86a0a7ae9d2493a837d02f321df7cb1387cded
-rw-r--r--res/layout/call_button_fragment.xml1
-rw-r--r--res/values/cm_strings.xml2
-rw-r--r--src/com/android/incallui/Call.java5
-rw-r--r--src/com/android/incallui/CallCardFragment.java128
-rw-r--r--src/com/android/incallui/CallList.java11
-rw-r--r--src/com/android/incallui/CallRecorder.java256
-rw-r--r--src/com/android/incallui/InCallActivity.java59
-rw-r--r--src/com/android/incallui/InCallServiceImpl.java1
8 files changed, 332 insertions, 131 deletions
diff --git a/res/layout/call_button_fragment.xml b/res/layout/call_button_fragment.xml
index 33787b66..dafab337 100644
--- a/res/layout/call_button_fragment.xml
+++ b/res/layout/call_button_fragment.xml
@@ -166,7 +166,6 @@
android:background="@drawable/btn_overflow"
android:contentDescription="@string/onscreenOverflowText"
android:visibility="gone" />
-
</LinearLayout>
</LinearLayout>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 13f53e2a..442584b9 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -30,6 +30,8 @@
<!-- Text for the onscreen "Blacklist" button -->
<string name="onscreenBlacklistText">Blacklist</string>
+ <string name="call_recording_failed_message">Failed to start call recording</string>
+
<!-- Blacklist confirmation dialog -->
<string name="blacklist_dialog_title">Add to blacklist</string>
<string name="blacklist_dialog_message">Future calls from <xliff:g id="number">%s</xliff:g> will be blocked</string>
diff --git a/src/com/android/incallui/Call.java b/src/com/android/incallui/Call.java
index a018de0b..b8fe301f 100644
--- a/src/com/android/incallui/Call.java
+++ b/src/com/android/incallui/Call.java
@@ -385,6 +385,11 @@ public final class Call {
return mTelecommCall.getDetails().getConnectTimeMillis();
}
+ /** Gets the time when call was first constructed */
+ public long getCreateTimeMillis() {
+ return mTelecommCall.getDetails().getCreateTimeMillis();
+ }
+
public boolean isConferenceCall() {
return hasProperty(CallProperties.CONFERENCE);
}
diff --git a/src/com/android/incallui/CallCardFragment.java b/src/com/android/incallui/CallCardFragment.java
index 59c89417..8927e025 100644
--- a/src/com/android/incallui/CallCardFragment.java
+++ b/src/com/android/incallui/CallCardFragment.java
@@ -43,6 +43,7 @@ import android.text.format.DateUtils;
import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.Display;
+import android.text.format.DateUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -145,11 +146,39 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
private int mVideoAnimationDuration;
- private String mRecordingTime;
-
private static final int TTY_MODE_OFF = 0;
private static final int TTY_MODE_HCO = 2;
+ private CallRecorder.RecordingProgressListener mRecordingProgressListener =
+ new CallRecorder.RecordingProgressListener() {
+ @Override
+ public void onStartRecording() {
+ mRecordingTimeLabel.setText(DateUtils.formatElapsedTime(0));
+ if (mRecordingTimeLabel.getVisibility() != View.VISIBLE) {
+ AnimUtils.fadeIn(mRecordingTimeLabel, AnimUtils.DEFAULT_DURATION);
+ }
+ if (mRecordingIcon.getVisibility() != View.VISIBLE) {
+ AnimUtils.fadeIn(mRecordingIcon, AnimUtils.DEFAULT_DURATION);
+ }
+ }
+
+ @Override
+ public void onStopRecording() {
+ AnimUtils.fadeOut(mRecordingTimeLabel, AnimUtils.DEFAULT_DURATION);
+ AnimUtils.fadeOut(mRecordingIcon, AnimUtils.DEFAULT_DURATION);
+ }
+
+ @Override
+ public void onRecordingTimeProgress(final long elapsedTimeMs) {
+ long elapsedSeconds = (elapsedTimeMs + 500) / 1000;
+ mRecordingTimeLabel.setText(DateUtils.formatElapsedTime(elapsedSeconds));
+
+ // make sure this is visible in case we re-loaded the UI for a call in progress
+ mRecordingTimeLabel.setVisibility(View.VISIBLE);
+ mRecordingIcon.setVisibility(View.VISIBLE);
+ }
+ };
+
private static final String VOLUME_BOOST = "volume_boost";
private static final String RECORD_STATE_CHANGED =
@@ -192,13 +221,8 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
IntentFilter filter = new IntentFilter();
filter.addAction(RECORD_STATE_CHANGED);
- getActivity().registerReceiver(recorderStateReceiver, filter);
mInCallActivity = (InCallActivity)getActivity();
-
- if (mInCallActivity.isCallRecording()) {
- recorderHandler.sendEmptyMessage(MESSAGE_TIMER);
- }
}
@@ -257,10 +281,16 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mMoreMenuButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
+ Call call = CallList.getInstance().getActiveOrBackgroundCall();
+ if (call != null) {
+ updateMoreMenuByCall(call.getState());
+ }
mMoreMenu.show();
}
});
+ CallRecorder recorder = CallRecorder.getInstance();
+ recorder.addRecordingProgressListener(mRecordingProgressListener);
mFloatingActionButtonContainer = view.findViewById(
R.id.floating_end_call_action_button_container);
@@ -1198,11 +1228,14 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
final MenuItem stopRecord = menu.findItem(R.id.menu_stop_record);
final MenuItem addToBlacklist = menu.findItem(R.id.menu_add_to_blacklist);
- boolean isRecording = ((InCallActivity)getActivity()).isCallRecording();
- boolean isRecordEnabled = ((InCallActivity)getActivity()).isCallRecorderEnabled();
-
- boolean startEnabled = !isRecording && isRecordEnabled && state == Call.State.ACTIVE;
- boolean stopEnabled = isRecording && isRecordEnabled && state == Call.State.ACTIVE;
+ CallRecorder callRecorder = CallRecorder.getInstance();
+ boolean startEnabled = false;
+ boolean stopEnabled = false;
+ if (callRecorder.isEnabled()) {
+ boolean isRecording = callRecorder.isRecording();
+ startEnabled = !isRecording && state == Call.State.ACTIVE;
+ stopEnabled = isRecording && state == Call.State.ACTIVE;
+ }
boolean blacklistVisible = BlacklistUtils.isBlacklistEnabled(getActivity())
&& Call.State.isConnectingOrConnected(state);
@@ -1256,63 +1289,10 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
@Override
public void onDestroy() {
super.onDestroy();
- getActivity().unregisterReceiver(recorderStateReceiver);
+ CallRecorder recorder = CallRecorder.getInstance();
+ recorder.removeRecordingProgressListener(mRecordingProgressListener);
}
- private void showCallRecordingElapsedTime() {
- if (mRecordingTimeLabel.getVisibility() != View.VISIBLE) {
- AnimUtils.fadeIn(mRecordingTimeLabel, AnimUtils.DEFAULT_DURATION);
- }
-
- mRecordingTimeLabel.setText(mRecordingTime);
- }
-
- private BroadcastReceiver recorderStateReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!RECORD_STATE_CHANGED.equals(intent.getAction())) {
- return;
- }
-
- if (mInCallActivity.isCallRecording()) {
- recorderHandler.sendEmptyMessage(MESSAGE_TIMER);
- } else {
- mRecordingTimeLabel.setVisibility(View.GONE);
- mRecordingIcon.setVisibility(View.GONE);
- }
- }
- };
-
- private Handler recorderHandler = new Handler() {
-
- @Override
- public void handleMessage(Message msg) {
-
- switch (msg.what) {
- case MESSAGE_TIMER:
- if (!mInCallActivity.isCallRecording()) {
- break;
- }
-
- String recordingTime = mInCallActivity.getCallRecordingTime();
-
- if (!TextUtils.isEmpty(recordingTime)) {
- mRecordingTime = recordingTime;
- mRecordingTimeLabel.setVisibility(View.VISIBLE);
- showCallRecordingElapsedTime();
- mRecordingIcon.setVisibility(View.VISIBLE);
- }
-
- if (!recorderHandler.hasMessages(MESSAGE_TIMER)) {
- sendEmptyMessageDelayed(MESSAGE_TIMER, 1000);
- }
-
- break;
- }
- }
- };
-
private class MorePopupMenu extends PopupMenu implements PopupMenu.OnMenuItemClickListener {
public MorePopupMenu(Context context, View anchor) {
super(context, anchor);
@@ -1323,13 +1303,19 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
public boolean onMenuItemClick(MenuItem item) {
switch(item.getItemId()) {
case R.id.menu_start_record:
- ((InCallActivity)getActivity()).startInCallRecorder();
-
+ Call call = CallList.getInstance().getActiveCall();
+ // can't start recording with no active call
+ if (call != null) {
+ CallRecorder.getInstance().startRecording(
+ call.getNumber(), call.getCreateTimeMillis());
+ }
return true;
case R.id.menu_stop_record:
- ((InCallActivity)getActivity()).stopInCallRecorder();
-
+ CallRecorder callRecorder = CallRecorder.getInstance();
+ if (callRecorder.isRecording()) {
+ callRecorder.finishRecording();
+ }
return true;
case R.id.menu_add_to_blacklist:
diff --git a/src/com/android/incallui/CallList.java b/src/com/android/incallui/CallList.java
index e5c0d385..35eafea4 100644
--- a/src/com/android/incallui/CallList.java
+++ b/src/com/android/incallui/CallList.java
@@ -27,11 +27,13 @@ import android.telecom.Phone;
import android.telecom.PhoneAccountHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import com.android.internal.telephony.PhoneConstants;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
@@ -789,6 +791,15 @@ public class CallList implements InCallPhoneListener {
return retval;
}
+ public Call getCallWithStateAndNumber(int state, String number) {
+ for (Call call : mCallById.values()) {
+ if (TextUtils.equals(call.getNumber(), number) && call.getState() == state) {
+ return call;
+ }
+ }
+ return null;
+ }
+
public void addActiveSubChangeListener(ActiveSubChangeListener listener) {
Preconditions.checkNotNull(listener);
mActiveSubChangeListeners.add(listener);
diff --git a/src/com/android/incallui/CallRecorder.java b/src/com/android/incallui/CallRecorder.java
new file mode 100644
index 00000000..2afd8c34
--- /dev/null
+++ b/src/com/android/incallui/CallRecorder.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.incallui;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.services.callrecorder.CallRecorderService;
+import com.android.services.callrecorder.CallRecordingDataStore;
+import com.android.services.callrecorder.common.CallRecording;
+import com.android.services.callrecorder.common.ICallRecorderService;
+
+import java.util.Date;
+import java.util.HashSet;
+
+/**
+ * InCall UI's interface to the call recorder
+ *
+ * Manages the call recorder service lifecycle. We bind to the service whenever an active call
+ * is established, and unbind when all calls have been disconnected.
+ */
+public class CallRecorder implements CallList.Listener {
+ public static final String TAG = "CallRecorder";
+
+ private static CallRecorder sInstance = null;
+
+ private Context mContext;
+ private boolean mInitialized = false;
+ private ICallRecorderService mService = null;
+
+ private HashSet<RecordingProgressListener> mProgressListeners =
+ new HashSet<RecordingProgressListener>();
+ private Handler mHandler = new Handler();
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = ICallRecorderService.Stub.asInterface(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ public static CallRecorder getInstance() {
+ if (sInstance == null) {
+ sInstance = new CallRecorder();
+ }
+ return sInstance;
+ }
+
+ public boolean isEnabled() {
+ return CallRecorderService.isEnabled(mContext);
+ }
+
+ private CallRecorder() {
+ CallList.getInstance().addListener(this);
+ }
+
+ public void setUp(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ private void initialize() {
+ if (isEnabled() && !mInitialized) {
+ Intent serviceIntent = new Intent(mContext, CallRecorderService.class);
+ mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
+ mInitialized = true;
+ }
+ }
+
+ private void uninitialize() {
+ if (mInitialized) {
+ mContext.unbindService(mConnection);
+ mInitialized = false;
+ }
+ }
+
+ public boolean startRecording(final String phoneNumber, final long creationTime) {
+ if (mService == null) {
+ return false;
+ }
+
+ try {
+ if (mService.startRecording(phoneNumber, creationTime)) {
+ for (RecordingProgressListener l : mProgressListeners) {
+ l.onStartRecording();
+ }
+ mUpdateRecordingProgressTask.run();
+ return true;
+ } else {
+ Toast.makeText(mContext, R.string.call_recording_failed_message,
+ Toast.LENGTH_SHORT).show();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to start recording " + phoneNumber + ", " +
+ new Date(creationTime), e);
+ }
+
+ return false;
+ }
+
+ public boolean isRecording() {
+ if (mService == null) {
+ return false;
+ }
+
+ try {
+ return mService.isRecording();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Exception checking recording status", e);
+ }
+ return false;
+ }
+
+ public CallRecording getActiveRecording() {
+ if (mService == null) {
+ return null;
+ }
+
+ try {
+ return mService.getActiveRecording();
+ } catch (RemoteException e) {
+ Log.w("Exception getting active recording", e);
+ }
+ return null;
+ }
+
+ public void finishRecording() {
+ if (mService != null) {
+ try {
+ final CallRecording recording = mService.stopRecording();
+ if (recording != null) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ CallRecordingDataStore dataStore = new CallRecordingDataStore();
+ dataStore.open(mContext);
+ dataStore.putRecording(recording);
+ dataStore.close();
+ }
+ }).start();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to stop recording", e);
+ }
+ }
+
+ for (RecordingProgressListener l : mProgressListeners) {
+ l.onStopRecording();
+ }
+ mHandler.removeCallbacks(mUpdateRecordingProgressTask);
+ }
+
+ //
+ // Call list listener methods.
+ //
+ @Override
+ public void onIncomingCall(Call call) {
+ // do nothing
+ }
+
+ @Override
+ public void onCallListChange(final CallList callList) {
+ if (!mInitialized && callList.getActiveCall() != null) {
+ // we'll come here if this is the first active call
+ initialize();
+ } else {
+ // we can come down this branch to resume a call that was on hold
+ CallRecording active = getActiveRecording();
+ if (active != null) {
+ Call call = callList.getCallWithStateAndNumber(Call.State.ONHOLD,
+ active.phoneNumber);
+ if (call != null) {
+ // The call associated with the active recording has been placed
+ // on hold, so stop the recording.
+ finishRecording();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDisconnect(final Call call) {
+ CallRecording active = getActiveRecording();
+ if (active != null && TextUtils.equals(call.getNumber(), active.phoneNumber)) {
+ // finish the current recording if the call gets disconnected
+ finishRecording();
+ }
+
+ // tear down the service if there are no more active calls
+ if (CallList.getInstance().getActiveCall() == null) {
+ uninitialize();
+ }
+ }
+
+ @Override
+ public void onUpgradeToVideo(Call call) {}
+
+ // allow clients to listen for recording progress updates
+ public interface RecordingProgressListener {
+ public void onStartRecording();
+ public void onStopRecording();
+ public void onRecordingTimeProgress(long elapsedTimeMs);
+ }
+
+ public void addRecordingProgressListener(RecordingProgressListener listener) {
+ mProgressListeners.add(listener);
+ }
+
+ public void removeRecordingProgressListener(RecordingProgressListener listener) {
+ mProgressListeners.remove(listener);
+ }
+
+ private static final int UPDATE_INTERVAL = 500;
+
+ private Runnable mUpdateRecordingProgressTask = new Runnable() {
+ @Override
+ public void run() {
+ CallRecording active = getActiveRecording();
+ if (active != null) {
+ long elapsed = System.currentTimeMillis() - active.startRecordingTime;
+ for (RecordingProgressListener l : mProgressListeners) {
+ l.onRecordingTimeProgress(elapsed);
+ }
+ }
+ mHandler.postDelayed(mUpdateRecordingProgressTask, UPDATE_INTERVAL);
+ }
+ };
+}
diff --git a/src/com/android/incallui/InCallActivity.java b/src/com/android/incallui/InCallActivity.java
index ed5c394e..64180c83 100644
--- a/src/com/android/incallui/InCallActivity.java
+++ b/src/com/android/incallui/InCallActivity.java
@@ -72,14 +72,6 @@ public class InCallActivity extends Activity {
private static final String ACTION_SUPP_SERVICE_FAILURE =
"org.codeaurora.ACTION_SUPP_SERVICE_FAILURE";
- private static final Uri URI_PHONE_FEATURE = Uri
- .parse("content://com.qualcomm.qti.phonefeature.FEATURE_PROVIDER");
-
- private static final String METHOD_START_CALL_RECORD = "start_call_record";
- private static final String METHOD_STOP_CALL_RECORD = "stop_call_record";
- private static final String METHOD_IS_CALL_RECORD_RUNNING = "is_call_record_running";
- private static final String METHOD_IS_CALL_RECORD_AVAILABLE = "is_call_record_available";
- private static final String METHOD_GET_CALL_RECORD_DURATION = "get_call_record_duration";
private static final String EXTRA_RESULT = "result";
private CallButtonFragment mCallButtonFragment;
@@ -987,55 +979,4 @@ public class InCallActivity extends Activity {
}
}
}
-
- public Bundle callBinder(String method) {
- if (getContentResolver().acquireProvider(URI_PHONE_FEATURE) == null) {
- // Check whether phone feature enabled
- return null;
- }
-
- return getContentResolver().call(URI_PHONE_FEATURE, method, null, null);
- }
-
- public boolean isCallRecording() {
- boolean isRecording = false;
- Bundle result = callBinder(METHOD_IS_CALL_RECORD_RUNNING);
-
- if (result != null) {
- isRecording = result.getBoolean(EXTRA_RESULT);
- }
-
- return isRecording;
- }
-
- public boolean isCallRecorderEnabled() {
- boolean isCallRecorderEnabled = false;
- Bundle result = callBinder(METHOD_IS_CALL_RECORD_AVAILABLE);
-
- if (result != null) {
- isCallRecorderEnabled = result.getBoolean(EXTRA_RESULT);
- }
- return isCallRecorderEnabled;
- }
-
- public void startInCallRecorder() {
- callBinder(METHOD_START_CALL_RECORD);
- }
-
- public void stopInCallRecorder() {
- callBinder(METHOD_STOP_CALL_RECORD);
- }
-
- public String getCallRecordingTime() {
- long time = 0;
- Bundle result = callBinder(METHOD_GET_CALL_RECORD_DURATION);
-
- if (result != null) {
- time = result.getLong(EXTRA_RESULT) / 1000;
- }
-
- String recordingTime = String.format("%02d:%02d", time / 60, time % 60);
-
- return recordingTime;
- }
}
diff --git a/src/com/android/incallui/InCallServiceImpl.java b/src/com/android/incallui/InCallServiceImpl.java
index 37e16a3b..dfec2622 100644
--- a/src/com/android/incallui/InCallServiceImpl.java
+++ b/src/com/android/incallui/InCallServiceImpl.java
@@ -38,6 +38,7 @@ public class InCallServiceImpl extends InCallService {
getApplicationContext(),
CallList.getInstance(),
AudioModeProvider.getInstance());
+ CallRecorder.getInstance().setUp(getApplicationContext());
TelecomAdapter.getInstance().setContext(InCallServiceImpl.this);
}