Internal Services - Computer Science and...

Post on 30-Jun-2020

0 views 0 download

transcript

Internal Services

CSE 5236: Mobile Application DevelopmentInstructor: Adam C. Champion, Ph.D.

Course Coordinator: Dr. Rajiv Ramnath

1

Internal Services

• Communication: Email, SMS and telephony• Audio and video: Record and playback • Sensors: Accelerometer, light, magnetic,

ambient temperature

2

Sending Email: Javapublic void sendScoresViaEmail() {

Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);

emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,"Look at my AWESOME TicTacToe Score!");

// Can also fill To: using putExtra(..., EXTRA_EMAIL)emailIntent.setType("plain/text");emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,

firstPlayerName + " score is " + scorePlayerOne + " and " + secondPlayerName + " score is " + scorePlayerTwo);

startActivity(emailIntent);}

3

How to send email programmatically: http://www.oodlestechnologies.com/blogs/Send-Mail-in-Android-without-Using-Intent (or search online)

Sending Email: Kotlinfun sendScoresViaEmail() {

val emailIntent = Intent(Intent.ACTION_SEND)emailIntent.putExtra(Intent.EXTRA_SUBJECT,

"Look at my AWESOME TicTacToe Score!")emailIntent.type = "plain/text"emailIntent.putExtra(Intent.EXTRA_TEXT,

mFirstPlayerName + " score is " + mScorePlayerOne +" and " + mSecondPlayerName + " score is " + mScorePlayerTwo)

startActivity(emailIntent)}

4

SMS: Javapublic void sendScoresViaSMS() {

Intent SMSIntent = new Intent(Intent.ACTION_VIEW);SMSIntent.putExtra("sms_body",

"Look at my AWESOME TicTacToe Score!" +firstPlayerName + " score is " + scorePlayerOne + " and " +secondPlayerName + " score is " + scorePlayerTwo);

SMSIntent.setType("vnd.android-dir/mms-sms");startActivity(SMSIntent);

}

5

Can also use SMS class; see: http://developer.android.com/reference/android/telephony/SmsManager.html .You need <uses-permission android:name=”android.permission.SEND_SMS”/>

SMS: Kotlinfun sendScoresViaSMS() {

val SMSIntent = Intent(Intent.ACTION_VIEW)SMSIntent.putExtra("sms_body",

"Look at my AWESOME TicTacToe Score!" + mFirstPlayerName + " score is " + mScorePlayerOne + " and " +mSecondPlayerName + " score is " + mScorePlayerTwo)

SMSIntent.type = "vnd.android-dir/mms-sms"startActivity(SMSIntent)

}

6

Telephony: Javapublic void callTicTacToeHelp() {

Intent phoneIntent = new Intent(Intent.ACTION_DIAL);

String phoneNumber = "842-822-4357"; // TIC TAC HELP

String uri = "tel:" + phoneNumber.trim();phoneIntent.setData(Uri.parse(uri));startActivity(phoneIntent);

}

Needs: <uses-permission android:name="android.permission.CALL_PHONE"/>

7

Telephony: Kotlinfun callTicTacToeHelp() {

val phoneIntent = Intent(Intent.ACTION_DIAL)val phoneNumber = "842-822-4357" // TIC TAC HELPval uri = "tel:" + phoneNumber.trim { it <= ' ' }phoneIntent.data = Uri.parse(uri)startActivity(phoneIntent)

}

8

Playing Audio Example: Setup

9

<?xml version="1.0" encoding="utf-8"?><LinearLayout ... >

<Button ... android:text="Start Audio"/><Button ... android:text="Stop Audio”/><Button ... android:text="Record Audio"/><Button ... android:text="Exit" />

</LinearLayout>

1.

2.

View device file system in Android Studio. Transfer files via your computer’s OS (ensure drivers are installed first).

Media file is sampleAudio.mp3 in external storage “music directory” (varies among devices).

Next slides show AudioFragment code (Java and Kotlin).

// AudioFragment.javaprivate String mAudioFilePath = Environment.getExternalStoragePublicDirectory(

Environment.DIRECTORY_MUSIC).getPath() + File.separator + "sample_audio.mp3";private Intent mRecordAudioIntent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)private Uri mAudioFileUri; // . . .@Overridepublic void onClick(View view) {

switch (view.getId()) {case R.id.buttonAudioStart:

if (!mStarted) {Intent musicIntent = new Intent(getActivity().getApplicationContext(),

MediaPlaybackService.class);musicIntent.putExtra("URIString", mAudioFileUri.toString());Log.d(TAG, "URI: " + mAudioFileUri.toString());getActivity().startService(musicIntent);mStarted = true; }

break;case R.id.buttonAudioStop:

getActivity().stopService(new Intent(getActivity().getApplicationContext(), MediaPlaybackService.class));

mStarted = false;break;

case R.id.buttonAudioRecord:startActivityForResult(mRecordAudioIntent, AUDIO_CAPTURED);break;

case R.id.buttonAudioExit:getActivity().finish();break; }

}

public void onActivityResult(int requestCode, int resultCode, Intent data) {if (resultCode == RESULT_OK && requestCode == AUDIO_CAPTURED) {

mAudioFileUri = data.getData(); }} 10

1

2

3

// AudioFragment.ktprivate val mAudioFilePath = Environment.getExternalStoragePublicDirectory(

Environment.DIRECTORY_MUSIC).path + File.separator + "sample_audio.mp3"private lateinit var mAudioFileUri: Uriprivate val mRecordAudioIntent = Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)override fun onClick(view: View) {

when (view.id) {R.id.buttonAudioStart -> if (!mStarted) {

val musicIntent = Intent(activity.applicationContext, MediaPlaybackService::class.java)

musicIntent.putExtra("URIString", mAudioFileUri.toString())Log.d(TAG, "URI: " + mAudioFileUri.toString())activity.startService(musicIntent)mStarted = true

}R.id.buttonAudioStop -> {

activity.stopService(Intent(activity.applicationContext, MediaPlaybackService::class.java))

mStarted = false}R.id.buttonAudioRecord -> startActivityForResult(mRecordAudioIntent,

AUDIO_CAPTURED)R.id.buttonAudioExit -> activity.finish()

}}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

if (resultCode == RESULT_OK && requestCode == AUDIO_CAPTURED) {if (data != null) {

mAudioFileUri = data.data }}

}11

1

2

3

Media Player States

12Source: https://developer.android.com/reference/android/media/MediaPlayer.html

Idle

Initialized

reset()

PreparingprepareAsync()

Prepared

prepare()

Started

start()

Stopped Paused

PlaybackCompleted

prepareAsync()

OnPreparedListener.

onPrepared()

prepare()

stop()

stop()

start()

pause()

stop()

seekTo()

seekTo(), pause()

seekTo()

looping == true &&

playback completes

seekTo(), start()

start()

(from beginning)

looping == false

and onCompletion() invokedon OnCompletionListener

stop()

stop()

setDataSource()

Playing Audio: Service: Java<service android:enabled="true” android:name=".MediaPlaybackService”/>// MediaPlayerService.javapublic class MediaPlaybackService extends Service {

MediaPlayer player;

@Overridepublic IBinder onBind(Intent intent) { return null; }

@Overridepublic void onCreate() {

player = MediaPlayer.create(this, R.raw.sample_audio); player.setLooping(true);}

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {

super.onStartCommand(intent, flags, startId);Bundle extras = intent.getExtras();if (extras != null) {

String audioFileURIString = extras.getString("URIString");Uri audioFileURI = Uri.parse(audioFileURIString);try {

player.reset(); player.setDataSource(this.getApplicationContext(), audioFileURI);player.prepare(); player.start();

} catch (Exception e) {e.printStackTrace();

}}return START_STICKY;

}

@Overridepublic void onDestroy() { player.stop(); }

}

13

Playing Audio: Service: Kotlin<service android:enabled="true” android:name=".MediaPlaybackService”/>// MediaPlayerService.ktclass MediaPlaybackService : Service() {

internal lateinit var player: MediaPlayer

override fun onBind(intent: Intent): IBinder? { return null}

override fun onCreate() {player = MediaPlayer.create(this, R.raw.sample_audio)player.apply { isLooping = true }

}

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {super.onStartCommand(intent, flags, startId)val extras = intent.extrasif (extras != null) {

val audioFileURIString = extras.getString("URIString")val audioFileURI = Uri.parse(audioFileURIString)try {

player.reset()player.setDataSource(this.applicationContext, audioFileURI)player.prepare()player.start()

} catch (e: Exception) {e.printStackTrace()

}}

return Service.START_STICKY}

override fun onDestroy() { player.stop() }}

14

Handling Video Using VideoView<?xml version="1.0" encoding="utf-8"?><LinearLayout ... >

<VideoView android:id="@+id/videoView"android:layout_height="175dip"android:layout_width="match_parent"android:layout_gravity="center" />

<Button ... android:text="Start Video"/><Button ... android:text="Stop Video”/><Button ... android:text="Record Video"/><Button ... android:text="Exit" />

</LinearLayout>

15

// VideoFragment.javapublic class VideoFragment extends Fragment

implements View.OnClickListener {private Button mButtonStart, mButtonStop, mButtonRecord;private VideoView mVideoView = null;private Uri mVideoFileUri = null;private Intent mRecordVideoIntent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View v = inflater.inflate(R.layout.fragment_video, container, false);mVideoView = (VideoView) v.findViewById(R.id.videoView);mButtonStart = (Button) v.findViewById(R.id.buttonVideoStart); // Set onClickListener(this)mButtonStop = (Button) v.findViewById(R.id.buttonVideoStop); // Set onClickListener(this) mButtonRecord = (Button) v.findViewById(R.id.buttonVideoRecord); // Set onClickListener(this)

Button btnExit = (Button) v.findViewById(R.id.buttonVideoExit); // Set onClickListener(this)String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)

.getPath() + File.separator + "sample_video.mp4";File videoFile = new File(path);if (videoFile.exists()) { mVideoFileUri = Uri.fromFile(videoFile); }else {

// Video file doesn't exist, so load sample video from resources.String videoResourceName = "android.resource://" + getActivity().getPackageName() +

File.separator + R.raw.sample_video;mVideoFileUri = Uri.parse(videoResourceName);

}

// Guard against no audio recorder app (disable the "record" button).PackageManager packageManager = getActivity().getPackageManager();if (packageManager.resolveActivity(mRecordVideoIntent, PackageManager.MATCH_DEFAULT_ONLY) == null){ mButtonRecord.setEnabled(false); }

return v;}

Handling Video: Java (1)

16

Handling Video: Java (2)

17

// VideoFragment.java (continued)@Override

public void onClick(View view) {switch (view.getId()) {

case R.id.buttonVideoStart:// Load and start the moviemVideoView.setVideoURI(mVideoFileUri);mVideoView.start();break;

case R.id.buttonVideoRecord:startActivityForResult(mRecordVideoIntent, VIDEO_CAPTURED);break;

case R.id.buttonVideoStop:mVideoView.stopPlayback();break;

case R.id.buttonVideoExit:getActivity().finish();break;

}}

public void onActivityResult(int requestCode, int resultCode, Intent data) {if (resultCode == RESULT_OK && requestCode == VIDEO_CAPTURED) {

mVideoFileUri = data.getData();}

}}

// AudioFragment.ktclass VideoFragment : Fragment(), View.OnClickListener {

private lateinit var mButtonStart: Buttonprivate lateinit var mButtonStop: Buttonprivate lateinit var mButtonRecord: Buttonprivate lateinit var mVideoView: VideoViewprivate var mVideoFileUri: Uri? = nullprivate val mRecordVideoIntent = Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE)

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {val v = inflater.inflate(R.layout.fragment_video, container, false)mVideoView = v.findViewById<VideoView>(R.id.videoView)mButtonStart = v.findViewById<Button>(R.id.buttonVideoStart) // Set onClickLimButtonStop = v.findViewById<Button>(R.id.buttonVideoStop)mButtonRecord = v.findViewById<Button>(R.id.buttonVideoRecord)

val btnExit = v.findViewById<Button>(R.id.buttonVideoExit)

val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).path + File.separator + "sample_video.mp4"

val videoFile = File(path)if (videoFile.exists()) { mVideoFileUri = Uri.fromFile(videoFile) } else {

// Video file doesn't exist, so load sample video from resources.val videoResourceName = "android.resource://" + activity.packageName +

File.separator + R.raw.sample_videomVideoFileUri = Uri.parse(videoResourceName)

}

// Guard against no audio recorder app (disable the "record" button).val packageManager = activity.packageManagerif (packageManager.resolveActivity(mRecordVideoIntent, PackageManager.MATCH_DEFAULT_ONLY) == null) {

mButtonRecord.isEnabled = false}return v

}

Handling Video: Kotlin (1)

18

Handling Video: Kotlin (2)override fun onClick(view: View) {

when (view.id) {R.id.buttonVideoStart -> {

// Load and start the moviemVideoView.setVideoURI(mVideoFileUri)mVideoView.start()

}R.id.buttonVideoRecord -> startActivityForResult(mRecordVideoIntent,

VIDEO_CAPTURED)R.id.buttonVideoStop -> mVideoView.stopPlayback()R.id.buttonVideoExit -> activity.finish()

}}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {if (resultCode == RESULT_OK && requestCode == VIDEO_CAPTURED) {

if (data != null) {mVideoFileUri = data.data

}}

}

19

Handling Images: ImageView

<?xml version="1.0" encoding="utf-8"?><LinearLayout ... >

<ImageView android:id="@+id/imageView"android:layout_height="175dip"android:layout_width="match_parent"android:layout_gravity="center" />

<Button ... android:text="Show Image"/><Button ... android:text="Take Picture"/><Button ... android:text="Exit" />

</LinearLayout>

20

Handling Images: Java (1)// ImageFragment.javapublic class ImagesFragment extends Fragment implements View.OnClickListener {

private ImageView imageView = null;private static Uri imageFileURI;private String imageFilePath = Environment.getExternalStoragePublicDirectory(

Environment.DIRECTORY_PICTURES).getPath() + File.separator + "other_image.png";private Bitmap imageBitmap = null;private Intent mCaptureImageIntent = new Intent(

android.provider.MediaStore.ACTION_IMAGE_CAPTURE);

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {View v = inflater.inflate(R.layout.fragment_images, container, false);

imageView = (ImageView) v.findViewById(R.id.imageView);

Button buttonShow = (Button) v.findViewById(R.id.buttonImageShow);Button buttonCapture = (Button) v.findViewById(R.id.buttonImageCapture);Button buttonExit = (Button) v.findViewById(R.id.buttonImageExit);// Set up onClickListener(this) for the buttons

return v;}

21

Handling Images: Java(2)// ImageFragment.java (continued)@Overridepublic void onClick(View view) {

switch(view.getId()) {case R.id.buttonImageShow:

File imageFile = new File(imageFilePath);if (imageFile.exists()) {

imageBitmap = BitmapFactory.decodeFile(imageFilePath);imageView.setImageBitmap(imageBitmap);

} else {// File doesn't exist, so load a sample SVG image. imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);imageView.setImageResource(R.drawable.ic_scoreboard);

}break;

case R.id.buttonImageCapture: startActivityForResult(mCaptureImageIntent, IMAGE_CAPTURED); break;

case R.id.buttonImageExit: getActivity().finish(); break; }

}22

Handling Images: Java (3)

// ImageFragment.java (continued)public void onActivityResult(int requestCode, int resultCode, Intent cameraIntent) {

if (resultCode == RESULT_OK && requestCode == IMAGE_CAPTURED) {Bundle extras = cameraIntent.getExtras();if (extras != null) {

imageBitmap = (Bitmap) extras.get("data");imageView.setImageBitmap(imageBitmap);

}}

}

23

Memory management is critical for Bitmaps! Consider using an LRU cache or library such as Glide (https://github.com/bumptech/glide) to handle them. See https://developer.android.com/topic/performance/graphics/index.html for more info.

See also: https://issuetracker.google.com/issues/36917456

Handling Images: Kotlin (1)// ImagesFragment.ktclass ImagesFragment : Fragment(), View.OnClickListener {

private lateinit var imageView: ImageViewprivate val imageFilePath = Environment.getExternalStoragePublicDirectory(

Environment.DIRECTORY_PICTURES).path + File.separator + "other_image.png"private lateinit var imageBitmap: Bitmapprivate lateinit var imageFileURI: Uri

private val mCaptureImageIntent = Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE)

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {val v = inflater.inflate(R.layout.fragment_images, container, false)

imageView = v.findViewById<ImageView>(R.id.imageView) as ImageView

val buttonShow = v.findViewById<Button>(R.id.buttonImageShow) as Buttonval buttonCapture = v.findViewById<Button>(R.id.buttonImageCapture) as Buttonval buttonExit = v.findViewById<Button>(R.id.buttonImageExit) as Button// Set onClickListener(this) for each Button

return v} // . . .

24

Handling Images: Kotlin (2)// ImagesFragment.kt (continued)override fun onClick(view: View) {

when (view.id) {R.id.buttonImageShow -> {

val imageFile = File(imageFilePath)if (imageFile.exists()) {

imageBitmap = BitmapFactory.decodeFile(imageFilePath)imageView.setImageBitmap(imageBitmap)

} else {// File doesn't exist, so load a sample SVG image.// Disable hardware acceleration for SVGsimageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)imageView.setImageResource(R.drawable.ic_scoreboard)

}}R.id.buttonImageCapture -> startActivityForResult(mCaptureImageIntent,

IMAGE_CAPTURED)R.id.buttonImageExit -> activity.finish()

}} 25

Handling Images: Kotlin (3)// ImagesFragment.kt (continued)override fun onActivityResult(requestCode: Int, resultCode: Int, cameraIntent: Intent?) {

if (resultCode == RESULT_OK && requestCode == IMAGE_CAPTURED) {val extras = cameraIntent?.extrasif (extras != null) {

imageBitmap = extras.get("data") as BitmapimageView.setImageBitmap(imageBitmap)

}}

}

26

Sensors• Uses:

– Provide contextual and environmental data to app– Tailor app to environment, how people are using devices

• Example Tic-Tac-Toe files:– SensorsFragment class– fragment_sensors.xml, list_item_sensor.xml

• Issues:– Noisy sensor data on real-world devices – Best tested on real devices. To simulate sensors on the emulator see:

https://github.com/openintents/sensorsimulator– Inexpensive real devices: Moto E (4th gen.), Moto G (5th gen.). See:

http://thewirecutter.com/reviews/best-budget-android-phone, Amazon, eBay

27

Type ExamplesMotion Accelerometer, gyroscopeEnvironmental Light, temperature, humidity, barometric pressureMiscellaneous Camera, microphone, fingerprint, infrared

Displaying Sensors• Display all device sensors

(and their info) in a RecyclerView

• RecyclerView: displays (possibly large) dynamic list/grid of “items” with limited memory footprint

• More info: https://developer.android.com/guide/topics/ui/layout/recyclerview.html

28

Views

RecyclerView Workflow

29Source: Figs. 8.6–8.7, Bill Phillips, Chris Stewart, and Kristin Marsicano, Android Programming: The Big Nerd Ranch Guide, 3rd ed., 2017.

Listing Available Sensors: Javaprivate RecyclerView mSensorRecyclerView;private SensorAdapter mSensorAdapter;private SensorManager mSensorManager;private List<Sensor> mSensorList;

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {View v = inflater.inflate(R.layout.fragment_sensor_list, container, false);

mSensorRecyclerView = (RecyclerView) v.findViewById(R.id.sensor_recycler_view);mSensorRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

mSensorManager = (SensorManager) getActivity().getSystemService(SENSOR_SERVICE);if (mSensorManager != null) {

mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);mAdapter = new SensorAdapter(mSensorList); // “Adapts” sensor info tomSensorRecyclerView.setAdapter(mAdapter); // RecyclerView

}

return v;}

30

Listing Available Sensors: Kotlinprivate lateinit var mSensorRecyclerView: RecyclerViewprivate lateinit var mAdapter: SensorAdapterprivate lateinit var mSensorManager: SensorManagerprivate lateinit var mSensorList: List<Sensor>private var lastSensorValues = Hashtable<String, FloatArray>()

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {val v = inflater.inflate(R.layout.fragment_sensor_list, container, false)

mSensorRecyclerView = v.findViewById<RecyclerView>(R.id.sensor_recycler_view)mSensorRecyclerView.layoutManager = LinearLayoutManager(activity)

mSensorManager = activity.getSystemService(SENSOR_SERVICE) as SensorManagermSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL)mAdapter = SensorAdapter(mSensorList) // Adapts mSensorRecyclerView.adapter = mAdapter

return v}

31

Sensor Holder

Javaprivate class SensorHolder extends RecyclerView.ViewHolder {

private Sensor mSensor;private String mDescriptionStr;private TextView mSensorInfoTextView;

public SensorHolder(LayoutInflaterinflater, ViewGroup parent) {

super(inflater.inflate(R.layout.list_item_sensor, parent, false));

mSensorInfoTextView = (TextView) itemView.findViewById(R.id.sensor_data);

}

public void bind(Sensor sensor) {mSensor = sensor;

mDescriptionStr = getSensorDescription(sensor);

mSensorInfoTextView.setText(mDescriptionStr);

}}

Kotlinprivate inner class SensorHolder(

inflater: LayoutInflater, parent: ViewGroup) : RecyclerView.ViewHolder(

inflater.inflate(R.layout.list_item_sensor, parent, false)) {private lateinit var mSensor: Sensorprivate lateinit var mDescriptionStr: Stringprivate val mSensorInfoTextView: TextView

init { mSensorInfoTextView = itemView.findViewById<TextView>(R.id.sensor_data)

}

fun bind(sensor: Sensor) {mSensor = sensormDescriptionStr = getSensorDescription(

sensor)mSensorInfoTextView.text = mDescriptionStr

}}

32

Sensor AdapterJava

private class SensorAdapter extends RecyclerView.Adapter<SensorHolder> {

private List<Sensor> mSensorList;public SensorAdapter(List<Sensor> sensorList) {

mSensorList = sensorList; }

@Override public SensorHolder onCreateViewHolder(

ViewGroup parent, int viewType) {LayoutInflater inflater = LayoutInflater.from(getActivity());return new SensorHolder(inflater, parent);

}

@Overridepublic void onBindViewHolder(SensorHolder holder,

int position) {Sensor sensor = SensorsFragment.this.mSensorList

.get(position);String sensorDescription = getSensorDescription(

sensor);holder.bind(sensor);

}

@Overridepublic int getItemCount() {

return mSensorList.size(); }}

Kotlinprivate inner class SensorAdapter(

private val mSensorList: List<Sensor>) : RecyclerView.Adapter<SensorHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SensorHolder {

val inflater = LayoutInflater.from(activity)

return SensorHolder(inflater, parent) }

override fun onBindViewHolder(holder: SensorHolder, position: Int) {

val sensor = this@SensorsFragment.mSensorList[position]

holder.bind(sensor)}

override fun getItemCount(): Int {return mSensorList.size

}} 33

Registering Sensor Updates

Java@Overridepublic void onResume() {

super.onResume();// . . .// Start listening to sensor updatesfor (Sensor sensor : mSensorList) {

mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);

}}// . . .@Overridepublic void onPause() {

super.onPause();// Stop updates when pausedmSensorManager.unregisterListener(this);

}

Kotlinoverride fun onResume() {

super.onResume()// . . .// Start listening to sensor updatesfor (sensor in mSensorList) {

mSensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_NORMAL)

}}// . . .override fun onPause() {

super.onPause()// Stop updates when pausedmSensorManager.unregisterListener(this)

}

34

Receiving Sensor Updates

Java@Overridepublic void onSensorChanged(

SensorEvent sensorEvent) {String sensorEventString =

sensorEventToString(sensorEvent);// . . .Log.d(TAG, "--- EVENT Raw Values ---\n” +

sensorName + "\n" + "Distance Last = >” + distanceOfLastValue + "<\n" +"Distance This = >" + distanceOfThisValue + "<\n" +"Change = >" + change + "<\n" +"Percent = >" + percentageChange + "%\n" + "Last value = " + lastValueString + "<\n" +sensorEventString);

}

Kotlinoverride fun onSensorChanged(

sensorEvent: SensorEvent) {val sensorEventString =

sensorEventToString(sensorEvent)// . . .Log.d(TAG, "--- EVENT Raw Values ---\n" +

sensorName + "\nDistance Last= >" +distanceOfLastValue + "<\n" + "Distance This= >" + distanceOfThisValue + "<\n" + "Change = >" + change + "<\n" + "Percent = >" + percentageChange + "%\n" + "Last value = " + lastValueString + "<\n" + sensorEventString)

}

35See complete method for how to filter out noise.

Extracting Sensor Parameters

Javapublic String getSensorDescription(

Sensor sensor) {return "Sensor: " + sensor.getName() + "; Ver :" + sensor.getVersion() + "; Range: " + sensor.getMaximumRange() + "; Power: " + sensor.getPower() + "; Res: " + sensor.getResolution();

}

Kotlinfun getSensorDescription(

sensor: Sensor): String {return "Sensor: " + sensor.name + "; Ver :" + sensor.version + "; Range: " + sensor.maximumRange + "; Power: " + sensor.power + "; Res: " + sensor.resolution

}

36

References• Chapter 11: “Harnessing the Capabilities of your Android Device” from Android SDK 3

Programming for Dummies• Chapter 8 from Android Programming: The Big Nerd Ranch Guide, 3rd ed. (RecyclerView)• Services: http://developer.android.com/guide/topics/fundamentals/services.html• SMS: http://developer.android.com/reference/android/telephony/SmsManager.html• SIP (internet telephony): http://developer.android.com/reference/android/net/sip/package-

summary.html• MediaPlayer: http://developer.android.com/reference/android/media/MediaPlayer.html• MediaRecorder: http://developer.android.com/reference/android/media/MediaRecorder.html• MediaStore class (extract metadata from media):

http://developer.android.com/reference/android/provider/MediaStore.html• Camera: http://developer.android.com/reference/android/hardware/Camera.html• BitmapFactory: http://developer.android.com/reference/android/graphics/BitmapFactory.html• Bitmap: http://developer.android.com/reference/android/graphics/Bitmap.html• Sensor: http://developer.android.com/reference/android/hardware/Sensor.html• SensorEvent:

http://developer.android.com/reference/android/hardware/SensorEventListener.html

37

Thank You

Questions and comments?

38