Upload
sangita-ajay
View
222
Download
4
Embed Size (px)
DESCRIPTION
call recorder report
Citation preview
K.T.S.P. MANDAL’S
K.M.C COLLEGE, KHOPOLI
DEPARTMENT OF COMPUTER SCIENCEKHOPOLI-410203
A PROJECT REPORT
ON
Call Recorder
UNDER THE GUIDANCEOF
Miss. Shipra Dutta & Miss. Priyanka Sorte
SUBMITTED TO
UNIVERSITY OF MUMBAI
BY
Mr. KAMBLE PRATIK RAMESH.
M.SC.Part-II (COMPUTER SCIENCE)
2014-2015
1
2
Acknowledgement
My accomplishment would be incomplete without mentioning the efforts taken by all my mentors. I am grateful to them for their continual support, co-operation and guidance which have encouraged me to complete my project successfully and in due time. I hereby take opportunity to express my gratitude for their invaluable guidance and support.
This is my first milestone in M.Sc-II Computer Science. I would like to thank our Principal Mr. N. B. Pawar Sir and Prof. Mr. P. P. Wadkar (H.O.D. of Computer Science), who helped me throughout the project.
At the outset, I would like to thank all teachers who help me throughout the project. I would also especially like to acknowledge the help and guidance provided by Prof. Miss. Shipra Dutta &Prof. Miss. Priyanka Sorte in all place during the presentation of this projects I would like to express my thanks to my family and all my friends who have encouraged and motivated me throughout the project.Thanking you.
Mr. Kamble Pratik R.
M.sc-Part-II (Computer Science)
3
INDEX
4
INDEXSr. No Description Page no.
1. PRELIMINARY INVESTIGATION
1) Description & Limitations2) Proposed System3) Some Methods, Event listener & Handler4) AdvantageDisadvantage5) System Implementation Hardware and Software
Used6) Testing Methodology
6-16
2. System Analysis
1) Event Table2) Use Case Diagram3) Activity Diagram4) Class Diagram5) Sequence Diagram6) State Diagram7) Test Cases
17-25
3. Program Listing
1) Forms Designing2) Forms Designing With Their Code
26-49
4. Future Enhancement 50
5. References And Bibliography 51
5
PreliminaryInvestigatio
n
6
DISCRIPTION
This is Call Recorder application. It is a fully developed in Android Studio. When one person call to another person automatically call will be recorded. Only one thing we have to do, just enable check mark of the Records calls. So in an entire day whatever outgoing call we make or we get incoming call, All conversation will be recorded.
Many android phone does not support this facility, and those who have ,first they must have to click record option, then call will be recorded. There is an menu of audio source type, by clicking that we get a different option like mic, voice_call, voice_uplink, voice_downlink.So we can choose one of them, by default mice option is selected.
There is another menu for recording file format such as, Mp4, 3Gp and amr. So we can choose one of them. By default mp4 is selected. These menus are present in preference button. And all recorded call will be save in call log. When we click on call log button we get a list of recorded call.It is best use to make security.
7
LIMITATION
There is memory problem will cause due to save large amount of call recording .
If we not enable the check mark of records calls, then calls will not be recorded.
1)
8
PROPOSED SYSTEM
This is Call Recorder application. It is a fully developed in Android Studio. When one person call to another person automatically call will be recorded. Only one think we have to do, just enable check mark of the Records calls. So in an entire day whatever outgoing call we make or we get incoming call. All conversation will be recorded. There is an menu of audio source type, by clicking that we get a different option like mic, voice_call, voice_uplink, voice_downlink.So we can choose one of them, by default mice option is selected.
There is another menu for recording file format such as, Mp4, 3Gp and amr. So we can choose one of them. By default mp4 is selected. These menus are present in preference button. And all recorded call will be save in call log. When we click on call log button we get a list of recorded call.It is best use to make security.
9
Some Methods onCreate():This is the first callback and called when the activity is first
created. onStart():This callback is called when the activity becomes visible to the
user. onResume():This is called when the user starts interacting with the
application. onPause():The paused activity does not receive user input and cannot
execute any code and called when the current activity is being paused and the previous activity is being resumed.
onStop():This callback is called when the activity is no longer visible. onDestroy():This callback is called before the activity is destroyed by the
system. onRestart():This callback is called when the activity restarts after
stopping it onStartCommand():The system calls this method when another
component, such as an activity, requests that the service be started, by calling
startService():If you implement this method, it is your responsibility to stop the service when its work is done, by calling stopSelf() or stopService() methods.
onBind():The system calls this method when another component wants to bind with the service by calling
bindService(): If you implement this method, you must provide an interface that
clients use to communicate with the service, by returning an IBinder object. You must always implement this method, but if you don't want to allow binding, then you should return null.
onUnbind():The system calls this method when all clients have disconnected from a particular interface published by the service.
onRebind():The system calls this method when new clients have connected to the service, after it had previously been notified that all had disconnected in its onUnbind(Intent)
onCreate():The system calls this method when the service is first created using onStartCommand() or onBind(). This call is required to perform -
onetime setup. onDestroy():The system calls this method when the service is no longer
used and is being destroyed. Your service should implement this to clean up any resources such as threads, registered listeners, receivers, etc.
10
Event Listeners & Event Handlers onClick():OnClickListener()
This is called when the user either clicks or touches or focuses upon any widget like button, text, image etc. You will use onClick() event
handler to handle such event. onLongClick():OnLongClickListener()
This is called when the user either clicks or touches or focuses upon any widget like button, text, image etc. for one or more seconds.You will use onLongClick() event handler to handle such event.
onFocusChange():OnFocusChangeListener()This is called when the widget looses its focus ie. user goes away from the view item. You will use onFocusChange() event handler to handle such event.
onKey():OnFocusChangeListener()This is called when the user is focused on the item and presses or releases a hardware key on the device. You will use onKey() event handler to handle such event.
onTouch():OnTouchListener()This is called when the user presses the key, releases the key, or any movement gesture on the screen. You will use onTouch() event handler to handle such event.
SQLite:This open-source relational database engine is designed to be embedded in devices.
11
ADVANTAGES
1) It is best use for security purpose . 2) Even caller does not know that his call is recording.3) All recording process happens in background, so it is safe.
12
DISADVANTAGES
There is memory problem will cause due to save large amount of call recording.
If we not enable the check mark of records calls, then calls will not be recorded.
13
HARDWARE AND SOFTWARE USED
Hardware:
Utility : Samsung laptop, one android phone.
Software:
Operating System : Windows XP,Windows 7,windows 8. Supported Software : JDK 1.7,1.8,Android SDKFront End : Android studio.
14
TESTING METHODOLOGY
For testing purpose we have used white - box method - a widely usedTechnique in which path of logic are tested to determine how well they produce predictable results . With this commonly used tested technique, We have examined the internal structure of the object.
With the help of white – box testing we have tested the source code without taking into account the external description for that source code. Because ofthe use of this testing method we came to know about the unintentional items such as infinite loops, path through the code which should be allowed butwhich cannot be executed and dead (unreachable) code.
15
The key points that we have considered in white – box technique are as follows
Path Testing :In this we have tested every possible path in the code i.e. all condition to assure that Every line of code is working properly.
Condition Testing:In this we have tested for errors in condition ( Boolean, Arithmetic / Relational errors)
Data Flow Testing:In this have tested path according to location of definitions and their users .
Loop Testing:In this we have checked the validity of the loop constructs.
16
SYSTEM ANALYSIS
17
EVENT TABLE
The Event includes row and columns, representing events and their details effectively. Each row in the table records information about each event. Each column in the table represents key information about that event.
EVENT TRIGGER SOURCE ACTIVITY RESPONSE DESTINATION
Person make a outgoing call
calling Person Recording call
Call is recorded
On person device
Person make a outgoing call
calling Person Buzy tone Call is not recorded
On person device
Person receive call
Receive call Person Recording call
Call is recorded
On person device
USE CASE DIAGRAM 18
19
Make a call
Ringing
Receiver receive a call
Recording started
End call
Recording will stop
Save recording in call log
Person PersonDevice
Person Receiver
Activity Diagram
20
Make a outgoing call
Ringing
Check available
Start recording on person device
NO
YES
End call
Stop Recording
Save recording
Class Diagram
1
21
AudioPlayerControl
pause()start()destroy()seekTo(int pos)isPlaying()getDuration()
CallBroadcastReceiver
onReceive()getAction()
CallLog
clear()moveToFirst()loadRecordingsFromDir()onCreate()onStart()onRestart()onResume()playFile(String fName)onDestroy()
CallPlayer
getIntent()onDestroy()onPrepared(MediaPlayer mp)setMediaPlayer(aplayer)onCompletion(MediaPlayer mp)
CallRecorder
onCreate(Bundle savedInstanceState)getTabHost()setText(label)
PlayServiceonCreate()reset()setAudioStreamType()onDestroy()onBind()onRebind
Sequence Diagram
Make a outgoing call
Check availability
If available pickup the call
Start recording conversation
End a call and Stop recording
If not available
End a call without recording
22
: Caller Person : Receiver person
PreferencesonCreate(Bundle savedInstanceState)
State Diagram
Make a outgoing call
No
Yes
No
Yes
23
Check Availablity
End call
Start Recording
If End call
Still recording a call
Stop recording and
save
Test Cases :
--:Preference Button :--
Test ID :-01
Test Case Name :- Unit Testing
Test Type :- Black Box Testing
Purpose Of Test :- for getting correct output
Testing Objects :- Preference Button
Focus of Testing :- to check it is working fine or not
--: Test Process:--
Start Condition :- call recorder app should be present with preference button.
Initiation :- for initiation must be able to do followingthing
Attributes : - 1)in Record calls must able to mark on checkbox. 2)in audio source tab at least one option
one option should be selected. 3)Recording file format at least one option
should be selected.
24
--: Test Case :--
TC1)
1) Preferences Button: check mark Expected in records calls:If checkmark is enable call will be recorded
Expected Failure:- If checkmark is disable call will not be recorded
25
26
27
28
Coding:Android manifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.talentcodeworks.callrecorder" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="4"/> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:label="@string/app_name" android:icon="@drawable/icon"> <provider android:name=".RecordingProvider" android:authorities="com.talentcodeworks.callrecorder" /> <receiver android:name=".CallBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> <action android:name="android.intent.action.PHONE_STATE" /> <!-- <category android:name="android.intent.category.LAUNCHER" /> --> </intent-filter></receiver> <activity android:name=".CallRecorder" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".CallLog" android:label="CallLog"> </activity> <activity android:name=".CallPlayer"
29
android:label="CallPlayer"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <data android:mimeType="audio/*" /> </intent-filter> </activity> <activity android:name=".Preferences"> <intent-filter> <action android:name="com.talentcodeworks.callrecorder.ACTION_USER_PREFERENCE" /> </intent-filter></activity> <service android:name=".RecordService"> </service> <service android:name=".PlayService"></service></application></manifest>AudioPlaye Control.java
package com.talentcodeworks.callrecorder;import android.media.MediaPlayer;import android.widget.MediaController;import android.net.Uri;import android.util.Log;import android.widget.LinearLayout;
class AudioPlayerControl implements MediaController.MediaPlayerControl{ private static final String TAG = "CallRecorder"; private MediaPlayer player = null; private String path = null; public AudioPlayerControl(String path, CallPlayer listenerActivity) throws java.io.IOException { Log.i(TAG, "AudioPlayerControl constructed with path " + path); this.path = path; player = new MediaPlayer(); player.setDataSource(path); player.setOnPreparedListener(listenerActivity); player.setOnInfoListener(listenerActivity); player.setOnErrorListener(listenerActivity); player.setOnCompletionListener(listenerActivity); player.prepareAsync(); } public boolean canPause() { return true; } public boolean canSeekBackward() { return true; } public boolean canSeekForward() { return true; } @Override public int getAudioSessionId() { return 0; } public int getBufferPercentage() { Log.d(TAG, "AudioPlayerControl::getBufferPercentage returning 100"); return 100;
30
} public int getCurrentPosition() { int pos = player.getCurrentPosition(); Log.d(TAG, "AudioPlayerControl::getCurrentPosition returning " + pos); return pos; } public int getDuration() { int duration = player.getDuration(); Log.d(TAG, "AudioPlayerControl::getDuration returning " + duration); return duration; }
public boolean isPlaying() { boolean isp = player.isPlaying(); Log.d(TAG, "AudioPlayerControl::isPlaying returning " + isp); return isp; } public void pause() { Log.d(TAG, "AudioPlayerControl::pause"); player.pause(); } public void seekTo(int pos) { Log.d(TAG, "AudioPlayerControl::seekTo " + pos); player.seekTo(pos); } public void start() { Log.d(TAG, "AudioPlayerControl::start"); player.start(); } public void destroy() { Log.i(TAG, "AudioPlayerControll::destroy shutting down player"); if (player != null) { player.reset(); player.release(); player = null; } }} CallLog.java
import java.io.File;import java.util.ArrayList;import java.util.List;import java.util.Vector;import android.app.Activity;import android.content.SharedPreferences;import android.content.Intent;import android.content.Context;import android.content.SharedPreferences.Editor;
31
import android.content.ComponentName;import android.content.ContentResolver;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.preference.PreferenceManager;import android.util.Log;import android.view.View;import android.widget.ListView;import android.widget.MediaController;import android.widget.ArrayAdapter;import android.widget.AdapterView;
public class CallLog extends Activity{ private final String TAG = "CallRecorder"; private ListView fileList = null; private ArrayAdapter<String> fAdapter = null; private ArrayList<String> recordingNames = null; private MediaController controller = null; private void loadRecordingsFromProvider() { fAdapter.clear(); ContentResolver cr = getContentResolver(); Cursor c = cr.query(RecordingProvider.CONTENT_URI, null, null, null, null); String[] names = new String[c.getCount()]; int i = 0;
if (c.moveToFirst()) { do { fAdapter.add(c.getString(RecordingProvider.DETAILS_COLUMN)); i++; } while (c.moveToNext()); } fAdapter.notifyDataSetChanged(); }
private void loadRecordingsFromDir() { fAdapter.clear(); File dir = new File(RecordService.DEFAULT_STORAGE_LOCATION); String[] dlist = dir.list();
for (int i=0; i<dlist.length; i++) { fAdapter.add(dlist[i]); } fAdapter.notifyDataSetChanged(); }
32
private class CallItemClickListener implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { CharSequence s = (CharSequence)parent.getItemAtPosition(position); Log.w(TAG, "CallLog just got an item clicked: " + s); File f = new File(RecordService.DEFAULT_STORAGE_LOCATION + "/" + s.toString()); boolean useMediaController = true;
if (useMediaController) { Intent playIntent = new Intent(getApplicationContext(), CallPlayer.class); Uri uri = Uri.fromFile(f); playIntent.setData(uri); startActivity(playIntent); } else { playFile(s.toString()); } } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.call_log); fileList = (ListView)findViewById(R.id.play_file_list); Context context = getApplicationContext(); fAdapter = new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1); fileList.setAdapter(fAdapter); fileList.setOnItemClickListener(new CallItemClickListener()); } public void onStart() { super.onStart(); Log.i(TAG, "CallLog onStart"); }
public void onRestart() { super.onRestart(); Log.i(TAG, "CallLog onRestart"); } public void onResume() { super.onResume(); Log.i(TAG, "CallLog onResume about to load recording list again, does this work?");
loadRecordingsFromDir();
33
} private void playFile(String fName) { Log.i(TAG, "playFile: " + fName); Context context = getApplicationContext(); Intent playIntent = new Intent(context, PlayService.class); playIntent.putExtra(PlayService.EXTRA_FILENAME, RecordService.DEFAULT_STORAGE_LOCATION + "/" + fName); ComponentName name = context.startService(playIntent); if (null == name) { Log.w(TAG, "CallLog unable to start PlayService with intent: " + playIntent.toString()); } else { Log.i(TAG, "CallLog started service: " + name); } } public void onDestroy() { Context context = getApplicationContext(); Intent playIntent = new Intent(context, PlayService.class); context.stopService(playIntent); super.onDestroy(); }}
CallPlayer.java
import java.io.File;import android.app.Activity;import android.content.SharedPreferences;import android.content.Intent;import android.content.Context;import android.content.SharedPreferences.Editor;import android.content.ComponentName;import android.content.ContentResolver;import android.database.Cursor;import android.media.MediaPlayer;import android.os.Bundle;import android.preference.PreferenceManager;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.Button;import android.widget.MediaController;import android.widget.Toast;
public class CallPlayer extends Activity implements MediaPlayer.OnPreparedListener, MediaPlayer.OnInfoListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener{ private static final String TAG = "CallRecorder";
34
private AudioPlayerControl aplayer = null; private MediaController controller = null; private ViewGroup anchor = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.player); anchor = (ViewGroup)findViewById(R.id.playerlayout); if (aplayer != null) { Log.i(TAG, "CallPlayer onCreate called with aplayer already allocated, destroying old one."); aplayer.destroy(); aplayer = null; } if (controller != null) { Log.i(TAG, "CallPlayer onCreate called with controller already allocated, destroying old one."); controller = null; } Intent i = getIntent(); String path = i.getData().getEncodedPath(); Log.i(TAG, "CallPlayer onCreate with data: " + path); try { aplayer = new AudioPlayerControl(path, this); } catch (java.io.IOException e) { Log.e(TAG, "CallPlayer onCreate failed while creating AudioPlayerControl", e); Toast t = Toast.makeText(this, "CallPlayer received error attempting to create AudioPlayerControl: " + e, Toast.LENGTH_LONG); t.show(); finish(); } } public void onDestroy() { Log.i(TAG, "CallPlayer onDestroy"); if (aplayer != null) { aplayer.destroy(); aplayer = null; } super.onDestroy(); } private class MyMediaController extends MediaController { public MyMediaController(Context c, boolean ff) { super(c, ff); } } public void onPrepared(MediaPlayer mp) { Log.i(TAG, "CallPlayer onPrepared about to construct MediaController object");
35
controller = new MediaController(this, true); controller.setMediaPlayer(aplayer); controller.setAnchorView(anchor); controller.setEnabled(true); controller.show(0); //aplayer.getDuration()); } public boolean onInfo(MediaPlayer mp, int what, int extra) { Log.i(TAG, "CallPlayer onInfo with what " + what + " extra " + extra); return false; } public boolean onError(MediaPlayer mp, int what, int extra) { Log.e(TAG, "CallPlayer onError with what " + what + " extra " + extra); Toast t = Toast.makeText(this, "CallPlayer received error (what:" + what + " extra:" + extra + ") from MediaPlayer attempting to play ", Toast.LENGTH_LONG); t.show(); finish(); return true; } public void onCompletion(MediaPlayer mp) { Log.i(TAG, "CallPlayer onCompletion, finishing activity"); finish(); }}
CallRecorder.java
import android.app.TabActivity;import android.os.Bundle;import android.util.Log;import android.content.Intent;import android.view.View;import android.view.Menu;import android.content.ComponentName;import android.content.Context;import android.view.View;import android.view.LayoutInflater;import android.widget.LinearLayout;import android.widget.TabHost;import android.widget.TextView;
public class CallRecorder extends TabActivity{ private static final int MENU_UPDATE = Menu.FIRST; private static final int MENU_PREFERENCES = Menu.FIRST+1; private static final int SHOW_PREFERENCES = 1;
36
private static final String[] TABS = { "Preferences", "CallLog" }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);
setDefaultTab(0);
TabHost tabs = getTabHost();
for (int i=0; i<TABS.length; i++) { TabHost.TabSpec tab = tabs.newTabSpec(TABS[i]); ComponentName n = new ComponentName("com.talentcodeworks.callrecorder", "com.talentcodeworks.callrecorder." + TABS[i]); tab.setContent(new Intent().setComponent(n)); tab.setIndicator(TABS[i]); tabs.addTab(tab); } }
public static class MyTabIndicator extends LinearLayout { public MyTabIndicator(Context context, String label) { super(context); View tab = LayoutInflater.from(context).inflate(R.layout.tab_indicator, this); TextView tv = (TextView)tab.findViewById(R.id.tab_label); tv.setText(label); } }}
PlayService.java
import java.io.File;import java.io.IOException;import java.lang.Exception;import android.os.IBinder;import android.app.Service;import android.app.PendingIntent;import android.content.Context;import android.content.Intent;import android.media.AudioManager;import android.media.MediaPlayer;import android.widget.Toast;import android.util.Log;
37
import java.io.InputStream;import java.io.FileInputStream;import java.util.Iterator;
public class PlayService extends Service implements MediaPlayer.OnCompletionListener, MediaPlayer.OnInfoListener, MediaPlayer.OnErrorListener{ private static String TAG = "CallRecorder"; public static final String EXTRA_FILENAME = "filename"; private MediaPlayer player = null; private boolean isPlaying = false; private String recording = null; public void onCreate() { super.onCreate(); player = new MediaPlayer(); player.setOnCompletionListener(this); player.setOnInfoListener(this); player.setOnErrorListener(this); Log.i(TAG, "PlayService::onCreate created MediaPlayer object"); } public void onStart(Intent intent, int startId) { Log.i(TAG, "PlayService::onStart called while isPlaying:" + isPlaying); if (isPlaying) return; Context c = getApplicationContext(); recording = intent.getStringExtra(EXTRA_FILENAME); if (recording == null) { Log.w(TAG, "PlayService::onStart recording == null, returning"); return; } Log.i(TAG, "PlayService will play " + recording); try { player.reset(); player.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); player.setDataSource(recording); player.setLooping(false); player.prepare(); Log.d(TAG, "PlayService player.prepare() returned"); player.start(); isPlaying = true; Log.i(TAG, "player.start() returned"); } catch (java.io.IOException e) { Log.e(TAG, "PlayService::onStart() IOException attempting player.prepare()\n"); Toast t = Toast.makeText(getApplicationContext(), "PlayService was unable to start playing recording: " + e, Toast.LENGTH_LONG); t.show(); return; } catch (java.lang.Exception e) {
38
Toast t = Toast.makeText(getApplicationContext(), "CallRecorder was unable to start playing recording: " + e, Toast.LENGTH_LONG); t.show(); Log.e(TAG, "PlayService::onStart caught unexpected exception", e); } return; } public void onDestroy() { if (null != player) { Log.i(TAG, "PlayService::onDestroy calling player.release()"); isPlaying = false; player.release(); } super.onDestroy(); } public IBinder onBind(Intent intent) { return null; } public boolean onUnbind(Intent intent) { return false; } public void onRebind(Intent intent) { } public void onCompletion(MediaPlayer mp) { Log.i(TAG, "PlayService got MediaPlayer onCompletion callback"); isPlaying = false; } public boolean onInfo(MediaPlayer mp, int what, int extra) { Log.i(TAG, "PlayService got MediaPlayer onInfo callback with what: " + what + " extra: " + extra); return false; } public boolean onError(MediaPlayer mp, int what, int extra) { Log.e(TAG, "PlayService got MediaPlayer onError callback with what: " + what + " extra: " + extra); isPlaying = false; mp.reset(); return true; }}
Preferences.java
39
import android.content.SharedPreferences;import android.os.Bundle;import android.preference.PreferenceActivity;
public class Preferences extends PreferenceActivity{ static public final String PREF_RECORD_CALLS = "PREF_RECORD_CALLS"; static public final String PREF_AUDIO_SOURCE = "PREF_AUDIO_SOURCE"; static public final String PREF_AUDIO_FORMAT = "PREF_AUDIO_FORMAT";
SharedPreferences prefs;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.userpreferences); }}
RecordService.java
import java.io.File;import java.io.IOException;import java.lang.Exception;import java.util.Date;import java.text.SimpleDateFormat;import android.os.IBinder;import android.app.Service;import android.app.Notification;import android.app.NotificationManager;import android.app.PendingIntent;import android.preference.PreferenceManager;import android.content.SharedPreferences;import android.content.Context;import android.content.Intent;import android.media.MediaRecorder;import android.widget.Toast;import android.util.Log;import java.io.InputStream;import java.io.FileInputStream;import java.util.Iterator;
public class RecordService extends Service implements MediaRecorder.OnInfoListener, MediaRecorder.OnErrorListener{ private static final String TAG = "CallRecorder";
public static final String DEFAULT_STORAGE_LOCATION = "/sdcard/callrecorder";
40
private static final int RECORDING_NOTIFICATION_ID = 1;
private MediaRecorder recorder = null; private boolean isRecording = false; private File recording = null;;
private File makeOutputFile (SharedPreferences prefs) { File dir = new File(DEFAULT_STORAGE_LOCATION); if (!dir.exists()) { try { dir.mkdirs(); } catch (Exception e) { Log.e("CallRecorder", "RecordService::makeOutputFile unable to create directory " + dir + ": " + e); Toast t = Toast.makeText(getApplicationContext(), "CallRecorder was unable to create the directory " + dir + " to store recordings: " + e, Toast.LENGTH_LONG); t.show(); return null; } } else { if (!dir.canWrite()) { Log.e(TAG, "RecordService::makeOutputFile does not have write permission for directory: " + dir); Toast t = Toast.makeText(getApplicationContext(), "CallRecorder does not have write permission for the directory directory " + dir + " to store recordings", Toast.LENGTH_LONG); t.show(); return null; } } int audiosource = Integer.parseInt(prefs.getString(Preferences.PREF_AUDIO_SOURCE, "1")); prefix += "-channel" + audiosource + "-"; String suffix = ""; int audioformat = Integer.parseInt(prefs.getString(Preferences.PREF_AUDIO_FORMAT, "1")); switch (audioformat) { case MediaRecorder.OutputFormat.THREE_GPP: suffix = ".3gpp"; break; case MediaRecorder.OutputFormat.MPEG_4: suffix = ".mpg"; break; case MediaRecorder.OutputFormat.RAW_AMR: suffix = ".amr"; break; } try { return File.createTempFile(prefix, suffix, dir);
41
} catch (IOException e) { Log.e("CallRecorder", "RecordService::makeOutputFile unable to create temp file in " + dir + ": " + e); Toast t = Toast.makeText(getApplicationContext(), "CallRecorder was unable to create temp file in " + dir + ": " + e, Toast.LENGTH_LONG); t.show(); return null; } }
public void onCreate() { super.onCreate(); recorder = new MediaRecorder(); Log.i("CallRecorder", "onCreate created MediaRecorder object"); }
public void onStart(Intent intent, int startId) { Log.i("CallRecorder", "RecordService::onStartCommand called while isRecording:" + isRecording); if (isRecording) return; Context c = getApplicationContext(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); Boolean shouldRecord = prefs.getBoolean(Preferences.PREF_RECORD_CALLS, false); if (!shouldRecord) { Log.i("CallRecord", "RecordService::onStartCommand with PREF_RECORD_CALLS false, not recording"); return; } int audiosource = Integer.parseInt(prefs.getString(Preferences.PREF_AUDIO_SOURCE, "1")); int audioformat = Integer.parseInt(prefs.getString(Preferences.PREF_AUDIO_FORMAT, "1"));
recording = makeOutputFile(prefs); if (recording == null) { recorder = null; return; //return 0; }
Log.i("CallRecorder", "RecordService will config MediaRecorder with audiosource: " + audiosource + " audioformat: " + audioformat); try { recorder.reset(); recorder.setAudioSource(audiosource); Log.d("CallRecorder", "set audiosource " + audiosource); recorder.setOutputFormat(audioformat); Log.d("CallRecorder", "set output " + audioformat); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
42
Log.d("CallRecorder", "set encoder default"); recorder.setOutputFile(recording.getAbsolutePath()); Log.d("CallRecorder", "set file: " + recording); recorder.setOnInfoListener(this); recorder.setOnErrorListener(this); try { recorder.prepare(); } catch (java.io.IOException e) { Log.e("CallRecorder", "RecordService::onStart() IOException attempting recorder.prepare()\n"); Toast t = Toast.makeText(getApplicationContext(), "CallRecorder was unable to start recording: " + e, Toast.LENGTH_LONG); t.show(); recorder = null; return; //return 0; //START_STICKY; } Log.d("CallRecorder", "recorder.prepare() returned"); recorder.start(); isRecording = true; Log.i("CallRecorder", "recorder.start() returned"); updateNotification(true); } catch (java.lang.Exception e) { Toast t = Toast.makeText(getApplicationContext(), "CallRecorder was unable to start recording: " + e, Toast.LENGTH_LONG); t.show(); Log.e("CallRecorder", "RecordService::onStart caught unexpected exception", e); recorder = null; } return; //return 0; //return START_STICKY; } public void onDestroy() { super.onDestroy(); if (null != recorder) { Log.i("CallRecorder", "RecordService::onDestroy calling recorder.release()"); isRecording = false; recorder.release(); Toast t = Toast.makeText(getApplicationContext(), "CallRecorder finished recording call to " + recording, Toast.LENGTH_LONG); t.show(); }
updateNotification(false); } public IBinder onBind(Intent intent) { return null; }
43
public boolean onUnbind(Intent intent) { return false; }
public void onRebind(Intent intent) { }
private void updateNotification(Boolean status) { Context c = getApplicationContext(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); String ns = Context.NOTIFICATION_SERVICE; NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns); if (status) { int icon = R.drawable.rec; CharSequence tickerText = "Recording call from channel " + prefs.getString(Preferences.PREF_AUDIO_SOURCE, "1"); long when = System.currentTimeMillis(); Notification notification = new Notification(icon, tickerText, when); Context context = getApplicationContext(); CharSequence contentTitle = "CallRecorder Status"; CharSequence contentText = "Recording call from channel..."; Intent notificationIntent = new Intent(this, RecordService.class); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent); mNotificationManager.notify(RECORDING_NOTIFICATION_ID, notification); } else { mNotificationManager.cancel(RECORDING_NOTIFICATION_ID); } } public void onInfo(MediaRecorder mr, int what, int extra) { Log.i("CallRecorder", "RecordService got MediaRecorder onInfo callback with what: " + what + " extra: " + extra); isRecording = false; } public void onError(MediaRecorder mr, int what, int extra) { Log.e("CallRecorder", "RecordService got MediaRecorder onError callback with what: " + what + " extra: " + extra); isRecording = false; mr.release(); }}Coding for layout design
Call_log.xml
44
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:scrollbars="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Select file to play" /><ListView android:choiceMode="singleChoice" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/play_file_list" /></LinearLayout>
Main.xml
<?xml version="1.0" encoding="utf-8"?><TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="0px" android:layout_weight="1"/>
<TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content"/>
</LinearLayout>
</TabHost>Player.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/playerlayout" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <MediaController android:id="@+id/playercontroller" android:layout_width="fill_parent"
45
android:layout_height="fill_parent" />
</LinearLayout>
Preferences.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/recording_enabled_prompt"/> <CheckBox android:id="@+id/check_recording_enabled" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/audio_source_prompt"/> <Spinner android:id="@+id/spinner_audio_source" android:layout_width="fill_parent" android:layout_height="wrap_content" android:drawSelectorOnTop="true"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/audio_format_prompt"/> <Spinner android:id="@+id/spinner_audio_format" android:layout_width="fill_parent" android:layout_height="wrap_content" android:drawSelectorOnTop="true"/> <Button android:id="@+id/save_prefs" android:text="Save" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</RelativeLayout> Tab_indicator.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="0px" android:layout_height="wrap_content" android:layout_weight="1">
<TextView android:id="@+id/tab_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hmm"
46
android:layout_centerInParent="true" /></RelativeLayout>
Values
Arrays.xml<?xml version="1.0" encoding="utf-8"?><resources> <string-array name="audio_source_options"> <item>MIC</item> <item>VOICE_CALL</item> <item>VOICE_UPLINK</item> <item>VOICE_DOWNLINK</item> </string-array> <string-array name="audio_source_values"> <item>1</item> <item>4</item> <item>2</item> <item>3</item> </string-array> <string-array name="audio_format_options"> <item>MPEG_4</item> <item>RAW_AMR</item> <item>THREE_GPP</item> </string-array> <string-array name="audio_format_values"> <item>2</item> <item>3</item> <item>1</item> </string-array>
</resources>
Strings.xml
<?xml version="1.0" encoding="utf-8"?><resources> <string name="app_name">CallRecorder</string> <string name="recording_enabled_prompt">Record during calls</string> <string name="audio_source_prompt">Audio source</string> <string name="audio_format_prompt">Audio format</string></resources>
Xml
Userpreferences.xml
<?xml version="1.0" encoding="utf-8"?>
47
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <CheckBoxPreference android:key="PREF_RECORD_CALLS" android:title="Record calls" android:summary="Select to turn on recording of all calls" android:defaultValue="true" /> <ListPreference android:key="PREF_AUDIO_SOURCE" android:title="Audio source" android:summary="Select the audio source to record from during calls" android:entries="@array/audio_source_options" android:entryValues="@array/audio_source_values" android:dialogTitle="Audio source" android:defaultValue="1" /> <ListPreference android:key="PREF_AUDIO_FORMAT" android:title="Recording file format" android:summary="Select the file format to use for call recordings" android:entries="@array/audio_format_options" android:entryValues="@array/audio_format_values" android:dialogTitle="Audio format" android:defaultValue="1" /></PreferenceScreen>Callrecorder-master.iml
<?xml version="1.0" encoding="UTF-8"?><module type="JAVA_MODULE" version="4"> <component name="FacetManager"> <facet type="android" name="Android"> <configuration /> </facet> </component> <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true"/> </content> <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" /> <orderEntry type="sourceFolder" forTests="false" /> </component></module>
48
Future Enhancements
Number of call records will be unlimited to be stored in the mobile or can be able to upload in cloud.
GUI can be increase to look more attractive.
49
REFERENCES AND BIBLIOGRAPHY
Many projects and books have been referred to develop this project, but out of those many resources only a few of them have been very handy in the project development.
A few references are as follows:-
Android Studio tutorial Android Application Development FOR DUMmIES
By:- Donn Felker with Joshua Dobbs.
The Busy Coder's Guide to Android DevelopmenBy:- Mark L. Murphy.
Web sites: www.tutorialspoint.com www.google.com
50