internal services - computer science and...

38
Internal Services CSE 5236: Mobile Application Development Instructor: Adam C. Champion, Ph.D. Course Coordinator: Dr. Rajiv Ramnath 1

Upload: others

Post on 30-Jun-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

Internal Services

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

Course Coordinator: Dr. Rajiv Ramnath

1

Page 2: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

Internal Services

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

ambient temperature

2

Page 3: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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)

Page 4: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 5: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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”/>

Page 6: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 7: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 8: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 9: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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).

Page 10: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 11: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 12: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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()

Page 13: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 14: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 15: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 16: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 17: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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();}

}}

Page 18: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 19: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 20: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 21: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 22: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 23: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 24: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 25: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 26: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 27: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 28: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 29: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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.

Page 30: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 31: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 32: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 33: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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 = [email protected][position]

holder.bind(sensor)}

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

}} 33

Page 34: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 35: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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.

Page 36: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 37: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

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

Page 38: Internal Services - Computer Science and Engineeringweb.cse.ohio-state.edu/.../Lecture6_InternalServices.pdfInternal Services •Communication: Email, SMS and telephony •Audio and

Thank You

Questions and comments?

38