diff options
| author | Danny Baumann <dannybaumann@web.de> | 2016-02-01 14:06:44 +0100 |
|---|---|---|
| committer | Danny Baumann <dannybaumann@web.de> | 2016-02-10 13:18:21 +0100 |
| commit | 9cf4db0d7d71989d8ad70d27ac1665ed72046d14 (patch) | |
| tree | 0240f2f9ee89bbff0f1b187d100b800752ab7aab /src | |
| parent | 8f517b2c28cb29620fb32f1f8904b24528dde18d (diff) | |
| download | packages_apps_Dialer-9cf4db0d7d71989d8ad70d27ac1665ed72046d14.tar.gz packages_apps_Dialer-9cf4db0d7d71989d8ad70d27ac1665ed72046d14.tar.bz2 packages_apps_Dialer-9cf4db0d7d71989d8ad70d27ac1665ed72046d14.zip | |
Re-add call recording feature.
Change-Id: I47e9c49db56c75e5c5f491d2ef923039f7a7f519
Diffstat (limited to 'src')
8 files changed, 642 insertions, 4 deletions
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java index da86bd98b..842d68d43 100644 --- a/src/com/android/dialer/CallDetailActivity.java +++ b/src/com/android/dialer/CallDetailActivity.java @@ -63,6 +63,7 @@ import com.android.dialer.util.DialerUtils; import com.android.dialer.util.IntentUtil; import com.android.dialer.util.PhoneNumberUtil; import com.android.dialer.util.TelecomUtil; +import com.android.services.callrecorder.CallRecordingDataStore; import java.util.List; @@ -159,8 +160,8 @@ public class CallDetailActivity extends Activity invalidateOptionsMenu(); ListView historyList = (ListView) findViewById(R.id.history); - historyList.setAdapter( - new CallDetailHistoryAdapter(mContext, mInflater, mCallTypeHelper, details)); + historyList.setAdapter(new CallDetailHistoryAdapter(mContext, mInflater, + mCallTypeHelper, details, mCallRecordingDataStore)); String lookupKey = contactUri == null ? null : UriUtils.getLookupKeyFromUri(contactUri); @@ -226,6 +227,8 @@ public class CallDetailActivity extends Activity private boolean mHasEditNumberBeforeCallOption; private boolean mHasReportMenuOption; + private CallRecordingDataStore mCallRecordingDataStore = new CallRecordingDataStore(); + @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -267,6 +270,12 @@ public class CallDetailActivity extends Activity } @Override + protected void onDestroy() { + super.onDestroy(); + mCallRecordingDataStore.close(); + } + + @Override public void onResume() { super.onResume(); getCallDetails(); diff --git a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java index 3b488a8ae..5ce7b7da3 100644 --- a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java +++ b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java @@ -16,28 +16,47 @@ package com.android.dialer.calllog; +import android.content.ActivityNotFoundException; import android.content.Context; +import android.content.Intent; +import android.net.Uri; import android.provider.CallLog.Calls; +import android.text.TextUtils; +import android.text.format.DateFormat; import android.text.format.DateUtils; import android.text.format.Formatter; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.webkit.MimeTypeMap; import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.PopupMenu; import android.widget.TextView; +import android.widget.Toast; import com.android.contacts.common.CallUtil; import com.android.dialer.PhoneCallDetails; import com.android.dialer.R; import com.android.dialer.util.DialerUtils; +import com.android.services.callrecorder.common.CallRecording; +import com.android.services.callrecorder.CallRecorderService; +import com.android.services.callrecorder.CallRecordingDataStore; import com.google.common.collect.Lists; +import java.io.File; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; /** * Adapter for a ListView containing history items from the details of a call. */ -public class CallDetailHistoryAdapter extends BaseAdapter { +public class CallDetailHistoryAdapter extends BaseAdapter implements View.OnClickListener { /** The top element is a blank header, which is hidden under the rest of the UI. */ private static final int VIEW_TYPE_HEADER = 0; /** Each history item shows the detail of a call. */ @@ -48,17 +67,21 @@ public class CallDetailHistoryAdapter extends BaseAdapter { private final CallTypeHelper mCallTypeHelper; private final PhoneCallDetails[] mPhoneCallDetails; + private CallRecordingDataStore mCallRecordingDataStore; + /** * List of items to be concatenated together for duration strings. */ private ArrayList<CharSequence> mDurationItems = Lists.newArrayList(); public CallDetailHistoryAdapter(Context context, LayoutInflater layoutInflater, - CallTypeHelper callTypeHelper, PhoneCallDetails[] phoneCallDetails) { + CallTypeHelper callTypeHelper, PhoneCallDetails[] phoneCallDetails, + CallRecordingDataStore callRecordingDataStore) { mContext = context; mLayoutInflater = layoutInflater; mCallTypeHelper = callTypeHelper; mPhoneCallDetails = phoneCallDetails; + mCallRecordingDataStore = callRecordingDataStore; } @Override @@ -121,6 +144,7 @@ public class CallDetailHistoryAdapter extends BaseAdapter { TextView callTypeTextView = (TextView) result.findViewById(R.id.call_type_text); TextView dateView = (TextView) result.findViewById(R.id.date); TextView durationView = (TextView) result.findViewById(R.id.duration); + View playbackButton = result.findViewById(R.id.recording_playback_button); int callType = details.callTypes[0]; boolean isVideoCall = (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO @@ -143,9 +167,70 @@ public class CallDetailHistoryAdapter extends BaseAdapter { durationView.setText(formatDurationAndDataUsage(details.duration, details.dataUsage)); } + // do this synchronously to prevent recordings from "popping in" + // after detail item is displayed + List<CallRecording> recordings = null; + if (CallRecorderService.isEnabled(mContext)) { + mCallRecordingDataStore.open(mContext); // opens unless already open + recordings = mCallRecordingDataStore.getRecordings( + details.number.toString(), details.date); + } + playbackButton.setTag(recordings); + playbackButton.setOnClickListener(this); + playbackButton.setVisibility(recordings != null && !recordings.isEmpty() + ? View.VISIBLE : View.INVISIBLE); + return result; } + @Override + public void onClick(View view) { + if (view.getId() == R.id.recording_playback_button) { + final List<CallRecording> recordings = (List<CallRecording>) view.getTag(); + if (recordings.size() == 1) { + launchMediaPlayer(recordings.get(0).getFile()); + } else { + PopupMenu menu = new PopupMenu(mContext, view); + SimpleDateFormat format = getTimeWithSecondsFormat(); + for (int i = 0; i < recordings.size(); i++) { + final long startTime = recordings.get(i).startRecordingTime; + final String formattedDate = format.format(new Date(startTime)); + menu.getMenu().add(Menu.NONE, i, i, formattedDate); + } + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + CallRecording recording = recordings.get(item.getItemId()); + launchMediaPlayer(recording.getFile()); + return true; + } + }); + menu.show(); + } + } + } + + private SimpleDateFormat getTimeWithSecondsFormat() { + String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), + DateFormat.is24HourFormat(mContext) ? "Hmss" : "hmssa"); + return new SimpleDateFormat(pattern); + } + + private void launchMediaPlayer(File file) { + Uri uri = Uri.fromFile(file); + String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString()); + String mime = !TextUtils.isEmpty(extension) + ? MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) : "audio/*"; + try { + Intent intent = new Intent(Intent.ACTION_VIEW).setDataAndType(uri, mime); + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Toast.makeText(mContext, + R.string.call_playback_no_app_found_toast, + Toast.LENGTH_LONG).show(); + } + } + private CharSequence formatDuration(long elapsedSeconds) { long minutes = 0; long seconds = 0; diff --git a/src/com/android/dialer/settings/SoundSettingsFragment.java b/src/com/android/dialer/settings/SoundSettingsFragment.java index 341ab6fcb..102484fa0 100644 --- a/src/com/android/dialer/settings/SoundSettingsFragment.java +++ b/src/com/android/dialer/settings/SoundSettingsFragment.java @@ -38,6 +38,7 @@ import android.widget.Toast; import com.android.contacts.common.util.PermissionsUtil; import com.android.dialer.R; import com.android.phone.common.util.SettingsUtil; +import com.android.services.callrecorder.CallRecorderService; import java.lang.Boolean; import java.lang.CharSequence; @@ -131,6 +132,11 @@ public class SoundSettingsFragment extends PreferenceFragment getPreferenceScreen().removePreference(mDtmfToneLength); mDtmfToneLength = null; } + + if (!CallRecorderService.isEnabled(getActivity())) { + getPreferenceScreen().removePreference( + findPreference(context.getString(R.string.call_recording_category_key))); + } } @Override diff --git a/src/com/android/services/callrecorder/CallRecorderService.java b/src/com/android/services/callrecorder/CallRecorderService.java new file mode 100644 index 000000000..7b5602365 --- /dev/null +++ b/src/com/android/services/callrecorder/CallRecorderService.java @@ -0,0 +1,235 @@ +/* + * 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.services.callrecorder; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.media.MediaRecorder; +import android.media.MediaScannerConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.provider.Settings; +import android.util.Log; + +import com.android.services.callrecorder.common.CallRecording; +import com.android.services.callrecorder.common.ICallRecorderService; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.android.dialer.R; + +public class CallRecorderService extends Service { + private static final String TAG = "CallRecorderService"; + private static final boolean DBG = false; + + private static enum RecorderState { + IDLE, + RECORDING + }; + + private MediaRecorder mMediaRecorder = null; + private RecorderState mState = RecorderState.IDLE; + private CallRecording mCurrentRecording = null; + + private static final String AUDIO_SOURCE_PROPERTY = "persist.call_recording.src"; + + private SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyMMdd_HHmmssSSS"); + + private final ICallRecorderService.Stub mBinder = new ICallRecorderService.Stub() { + @Override + public CallRecording stopRecording() { + if (getState() == RecorderState.RECORDING) { + stopRecordingInternal(); + return mCurrentRecording; + } + return null; + } + + @Override + public boolean startRecording(String phoneNumber, long creationTime) + throws RemoteException { + String fileName = generateFilename(phoneNumber); + mCurrentRecording = new CallRecording(phoneNumber, creationTime, + fileName, System.currentTimeMillis()); + return startRecordingInternal(mCurrentRecording.getFile()); + + } + + @Override + public boolean isRecording() throws RemoteException { + return getState() == RecorderState.RECORDING; + } + + @Override + public CallRecording getActiveRecording() throws RemoteException { + return mCurrentRecording; + } + }; + + @Override + public void onCreate() { + if (DBG) Log.d(TAG, "Creating CallRecorderService"); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + private int getAudioSource() { + int defaultValue = getResources().getInteger(R.integer.call_recording_audio_source); + return SystemProperties.getInt(AUDIO_SOURCE_PROPERTY, defaultValue); + } + + private int getAudioFormatChoice() { + // This replicates PreferenceManager.getDefaultSharedPreferences, except + // that we need multi process preferences, as the pref is written in a separate + // process (com.android.dialer vs. com.android.incallui) + final String prefName = getPackageName() + "_preferences"; + final SharedPreferences prefs = getSharedPreferences(prefName, MODE_MULTI_PROCESS); + + try { + String value = prefs.getString(getString(R.string.call_recording_format_key), null); + if (value != null) { + return Integer.parseInt(value); + } + } catch (NumberFormatException e) { + // ignore and fall through + } + return 0; + } + + private synchronized boolean startRecordingInternal(File file) { + if (mMediaRecorder != null) { + if (DBG) { + Log.d(TAG, "Start called with recording in progress, stopping current recording"); + } + stopRecordingInternal(); + } + + if (checkSelfPermission(android.Manifest.permission.RECORD_AUDIO) + != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "Record audio permission not granted, can't record call"); + return false; + } + if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "External storage permission not granted, can't save recorded call"); + return false; + } + + if (DBG) Log.d(TAG, "Starting recording"); + + mMediaRecorder = new MediaRecorder(); + try { + int audioSource = getAudioSource(); + int formatChoice = getAudioFormatChoice(); + if (DBG) Log.d(TAG, "Creating media recorder with audio source " + audioSource); + mMediaRecorder.setAudioSource(audioSource); + mMediaRecorder.setOutputFormat(formatChoice == 0 + ? MediaRecorder.OutputFormat.AMR_WB : MediaRecorder.OutputFormat.MPEG_4); + mMediaRecorder.setAudioEncoder(formatChoice == 0 + ? MediaRecorder.AudioEncoder.AMR_WB : MediaRecorder.AudioEncoder.HE_AAC); + } catch (IllegalStateException e) { + Log.w(TAG, "Error initializing media recorder", e); + return false; + } + + file.getParentFile().mkdirs(); + String outputPath = file.getAbsolutePath(); + if (DBG) Log.d(TAG, "Writing output to file " + outputPath); + + try { + mMediaRecorder.setOutputFile(outputPath); + mMediaRecorder.prepare(); + mMediaRecorder.start(); + mState = RecorderState.RECORDING; + return true; + } catch (IOException e) { + Log.w(TAG, "Could not start recording for file " + outputPath, e); + } catch (IllegalStateException e) { + Log.w(TAG, "Could not start recording for file " + outputPath, e); + } catch (RuntimeException e) { + // only catch exceptions thrown by the MediaRecorder JNI code + if (e.getMessage().indexOf("start failed") >= 0) { + Log.w(TAG, "Could not start recording for file " + outputPath, e); + } else { + throw e; + } + } + + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + + return false; + } + + private synchronized void stopRecordingInternal() { + if (DBG) Log.d(TAG, "Stopping current recording"); + if (mMediaRecorder != null) { + try { + if (getState() == RecorderState.RECORDING) { + mMediaRecorder.stop(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + } + } catch (IllegalStateException e) { + Log.e(TAG, "Exception closing media recorder", e); + } + MediaScannerConnection.scanFile(this, new String[] { + mCurrentRecording.fileName + }, null, null); + mMediaRecorder = null; + mState = RecorderState.IDLE; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (DBG) Log.d(TAG, "Destroying CallRecorderService"); + } + + private synchronized RecorderState getState() { + return mState; + } + + private String generateFilename(String number) { + String timestamp = DATE_FORMAT.format(new Date()); + + if (TextUtils.isEmpty(number)) { + number = "unknown"; + } + + int formatChoice = getAudioFormatChoice(); + String extension = formatChoice == 0 ? ".amr" : ".m4a"; + return number + "_" + timestamp + extension; + } + + public static boolean isEnabled(Context context) { + return context.getResources().getBoolean(R.bool.call_recording_enabled); + } +} diff --git a/src/com/android/services/callrecorder/CallRecordingDataStore.java b/src/com/android/services/callrecorder/CallRecordingDataStore.java new file mode 100644 index 000000000..4d148a4fb --- /dev/null +++ b/src/com/android/services/callrecorder/CallRecordingDataStore.java @@ -0,0 +1,179 @@ +/* + * 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.services.callrecorder; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteStatement; +import android.provider.BaseColumns; +import android.util.Log; + +import com.android.services.callrecorder.common.CallRecording; + +import java.util.ArrayList; +import java.util.List; + +/** + * Persistent data store for call recordings. Usage: + * open() + * read/write operations + * close() + */ +public class CallRecordingDataStore { + private static final String TAG = "CallRecordingStore"; + private SQLiteOpenHelper mOpenHelper = null; + private SQLiteDatabase mDatabase = null; + + /** + * Open before reading/writing. Will not open handle if one is already open. + */ + public void open(Context context) { + if (mDatabase == null) { + mOpenHelper = new CallRecordingSQLiteOpenHelper(context); + mDatabase = mOpenHelper.getWritableDatabase(); + } + } + + /** + * close when finished reading/writing + */ + public void close() { + if (mDatabase != null) { + mDatabase.close(); + } + if (mOpenHelper != null) { + mOpenHelper.close(); + } + mDatabase = null; + mOpenHelper = null; + } + + /** + * Save a recording in the data store + * + * @param recording the recording to store + */ + public void putRecording(CallRecording recording) { + final String insertSql = "INSERT INTO " + + CallRecordingsContract.CallRecording.TABLE_NAME + " (" + + CallRecordingsContract.CallRecording.COLUMN_NAME_PHONE_NUMBER + ", " + + CallRecordingsContract.CallRecording.COLUMN_NAME_CALL_DATE + ", " + + CallRecordingsContract.CallRecording.COLUMN_NAME_RECORDING_FILENAME + ", " + + CallRecordingsContract.CallRecording.COLUMN_NAME_CREATION_DATE + ") " + + " VALUES (?, ?, ?, ?)"; + + try { + SQLiteStatement stmt = mDatabase.compileStatement(insertSql); + int idx = 1; + stmt.bindString(idx++, recording.phoneNumber); + stmt.bindLong(idx++, recording.creationTime); + stmt.bindString(idx++, recording.fileName); + stmt.bindLong(idx++, System.currentTimeMillis()); + long id = stmt.executeInsert(); + Log.i(TAG, "Saved recording " + recording + " with id " + id); + } catch (SQLiteException e) { + Log.w(TAG, "Failed to save recording " + recording, e); + } + } + + /** + * Get all recordings associated with a phone call + * + * @param phoneNumber phone number no spaces + * @param callCreationDate time that the call was created + * @return list of recordings + */ + public List<CallRecording> getRecordings(String phoneNumber, long callCreationDate) { + List<CallRecording> resultList = new ArrayList<CallRecording>(); + + final String query = "SELECT " + + CallRecordingsContract.CallRecording.COLUMN_NAME_RECORDING_FILENAME + "," + + CallRecordingsContract.CallRecording.COLUMN_NAME_CREATION_DATE + + " FROM " + CallRecordingsContract.CallRecording.TABLE_NAME + + " WHERE " + CallRecordingsContract.CallRecording.COLUMN_NAME_PHONE_NUMBER + " = ?" + + " AND " + CallRecordingsContract.CallRecording.COLUMN_NAME_CALL_DATE + " = ?" + + " ORDER BY " + CallRecordingsContract.CallRecording.COLUMN_NAME_CREATION_DATE; + + String args[] = { + phoneNumber, String.valueOf(callCreationDate) + }; + + try { + Cursor cursor = mDatabase.rawQuery(query, args); + while (cursor.moveToNext()) { + String fileName = cursor.getString(0); + long creationDate = cursor.getLong(1); + CallRecording recording = + new CallRecording(phoneNumber, callCreationDate, fileName, creationDate); + if (recording.getFile().exists()) { + resultList.add(recording); + } + } + cursor.close(); + } catch (SQLiteException e) { + Log.w(TAG, "Failed to fetch recordings for number " + phoneNumber + + ", date " + callCreationDate, e); + } + + return resultList; + } + + static class CallRecordingsContract { + static interface CallRecording extends BaseColumns { + static final String TABLE_NAME = "call_recordings"; + static final String COLUMN_NAME_PHONE_NUMBER = "phone_number"; + static final String COLUMN_NAME_CALL_DATE = "call_date"; + static final String COLUMN_NAME_RECORDING_FILENAME = "recording_filename"; + static final String COLUMN_NAME_CREATION_DATE = "creation_date"; + } + } + + static class CallRecordingSQLiteOpenHelper extends SQLiteOpenHelper { + private static final int VERSION = 1; + private static final String DB_NAME = "callrecordings.db"; + + public CallRecordingSQLiteOpenHelper(Context context) { + super(context, DB_NAME, null, VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + CallRecordingsContract.CallRecording.TABLE_NAME + " (" + + CallRecordingsContract.CallRecording._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + CallRecordingsContract.CallRecording.COLUMN_NAME_PHONE_NUMBER + " TEXT," + + CallRecordingsContract.CallRecording.COLUMN_NAME_CALL_DATE + " LONG," + + CallRecordingsContract.CallRecording.COLUMN_NAME_RECORDING_FILENAME + " TEXT, " + + CallRecordingsContract.CallRecording.COLUMN_NAME_CREATION_DATE + " LONG" + + ");" + ); + + db.execSQL("CREATE INDEX IF NOT EXISTS phone_number_call_date_index ON " + + CallRecordingsContract.CallRecording.TABLE_NAME + " (" + + CallRecordingsContract.CallRecording.COLUMN_NAME_PHONE_NUMBER + ", " + + CallRecordingsContract.CallRecording.COLUMN_NAME_CALL_DATE + ");" + ); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // implement if we change the schema + } + } +} diff --git a/src/com/android/services/callrecorder/common/CallRecording.aidl b/src/com/android/services/callrecorder/common/CallRecording.aidl new file mode 100644 index 000000000..5ab84a09e --- /dev/null +++ b/src/com/android/services/callrecorder/common/CallRecording.aidl @@ -0,0 +1,3 @@ +package com.android.services.callrecorder.common; + +parcelable CallRecording; diff --git a/src/com/android/services/callrecorder/common/CallRecording.java b/src/com/android/services/callrecorder/common/CallRecording.java new file mode 100644 index 000000000..0bb192f8d --- /dev/null +++ b/src/com/android/services/callrecorder/common/CallRecording.java @@ -0,0 +1,82 @@ +/* + * 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.services.callrecorder.common; + +import android.os.Environment; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.File; + +public final class CallRecording implements Parcelable { + public String phoneNumber; + public long creationTime; + public String fileName; + public long startRecordingTime; + + private static final String PUBLIC_DIRECTORY_NAME = "CallRecordings"; + + public static final Parcelable.Creator<CallRecording> CREATOR = new + Parcelable.Creator<CallRecording>() { + public CallRecording createFromParcel(Parcel in) { + return new CallRecording(in); + } + + public CallRecording[] newArray(int size) { + return new CallRecording[size]; + } + }; + + public CallRecording(String phoneNumber, long creationTime, + String fileName, long startRecordingTime) { + this.phoneNumber = phoneNumber; + this.creationTime = creationTime; + this.fileName = fileName; + this.startRecordingTime = startRecordingTime; + } + + public CallRecording(Parcel in) { + phoneNumber = in.readString(); + creationTime = in.readLong(); + fileName = in.readString(); + startRecordingTime = in.readLong(); + } + + public File getFile() { + File dir = Environment.getExternalStoragePublicDirectory(PUBLIC_DIRECTORY_NAME); + return new File(dir, fileName); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(phoneNumber); + out.writeLong(creationTime); + out.writeString(fileName); + out.writeLong(startRecordingTime); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "phoneNumber=" + phoneNumber + ", creationTime=" + creationTime + + ", fileName=" + fileName + ", startRecordingTime=" + startRecordingTime; + } +} diff --git a/src/com/android/services/callrecorder/common/ICallRecorderService.aidl b/src/com/android/services/callrecorder/common/ICallRecorderService.aidl new file mode 100644 index 000000000..d6d96d712 --- /dev/null +++ b/src/com/android/services/callrecorder/common/ICallRecorderService.aidl @@ -0,0 +1,39 @@ +package com.android.services.callrecorder.common; + +import com.android.services.callrecorder.common.CallRecording; + +/** + * Service for recording phone calls. Only one recording may be active at a time + * (i.e. every call to startRecording should be followed by a call to stopRecording). + */ +interface ICallRecorderService { + + /** + * Start a recording. + * + * @return true if recording started successfully + */ + boolean startRecording(String phoneNumber, long creationTime); + + /** + * stops the current recording + * + * @return call recording data including the output filename + */ + CallRecording stopRecording(); + + /** + * Recording status + * + * @return true if there is an active recording + */ + boolean isRecording(); + + /** + * Get recording currently in progress + * + * @return call recording object + */ + CallRecording getActiveRecording(); + +} |
