62
K.T.S.P. MANDAL’S K.M.C COLLEGE, KHOPOLI DEPARTMENT OF COMPUTER SCIENCE KHOPOLI-410203 A PROJECT REPORT ON Call Recorder UNDER THE GUIDANCE OF 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

Android Documentation

Embed Size (px)

DESCRIPTION

call recorder report

Citation preview

Page 1: Android Documentation

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

Page 2: Android Documentation

2

Page 3: Android Documentation

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

Page 4: Android Documentation

INDEX

4

Page 5: Android Documentation

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

Page 6: Android Documentation

PreliminaryInvestigatio

n

6

Page 7: Android Documentation

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

Page 8: Android Documentation

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

Page 9: Android Documentation

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

Page 10: Android Documentation

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

Page 11: Android Documentation

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

Page 12: Android Documentation

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

Page 13: Android Documentation

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

Page 14: Android Documentation

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

Page 15: Android Documentation

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

Page 16: Android Documentation

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

Page 17: Android Documentation

SYSTEM ANALYSIS

17

Page 18: Android Documentation

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

Page 19: Android Documentation

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

Page 20: Android Documentation

20

Make a outgoing call

Ringing

Check available

Start recording on person device

NO

YES

End call

Stop Recording

Save recording

Page 21: Android Documentation

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

Page 22: Android Documentation

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)

Page 23: Android Documentation

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

Page 24: Android Documentation

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

Page 25: Android Documentation

--: 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

Page 26: Android Documentation

26

Page 27: Android Documentation

27

Page 28: Android Documentation

28

Page 29: Android Documentation

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

Page 30: Android Documentation

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

Page 31: Android Documentation

} 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

Page 32: Android Documentation

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

Page 33: Android Documentation

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

Page 34: Android Documentation

} 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

Page 35: Android Documentation

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

Page 36: Android Documentation

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

Page 37: Android Documentation

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

Page 38: Android Documentation

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

Page 39: Android Documentation

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

Page 40: Android Documentation

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

Page 41: Android Documentation

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

Page 42: Android Documentation

} 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

Page 43: Android Documentation

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

Page 44: Android Documentation

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

Page 45: Android Documentation

<?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

Page 46: Android Documentation

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

Page 47: Android Documentation

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

Page 48: Android Documentation

<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

Page 49: Android Documentation

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

Page 50: Android Documentation

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