Internal Services CSE 5236: Mobile Application Development Instructor: Adam C. Champion, Ph.D. Course Coordinator: Dr. Rajiv Ramnath Reading: Big Nerd Ranch Guide, Chap. 15, 16 (Intents, camera); https://developer.android.com/docs/ (Sensors)
Internal Services Communication: Email, SMS and telephony Audio and video: Record and playback Sensors: Accelerometer, light, magnetic, ambient temperature
Sending Email: Java How to send email programmatically: public 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); } How to send email programmatically: http://www.oodlestechnologies.com/blogs/Send-Mail-in-Android-without-Using-Intent (or search online)
Sending Email: Kotlin fun 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) }
SMS: Java public 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); } 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: Kotlin fun 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) }
Telephony: Java Needs: public 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"/> Ask why telephony needs permissions. Because phone dials immediately.
Telephony: Kotlin fun callTicTacToeHelp() { val phoneIntent = Intent(Intent.ACTION_DIAL) val phoneNumber = "842-822-4357" // TIC TAC HELP val uri = "tel:" + phoneNumber.trim { it <= ' ' } phoneIntent.data = Uri.parse(uri) startActivity(phoneIntent) }
Playing Audio Example: Setup 1. <?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> 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. java private String mAudioFilePath = Environment // AudioFragment.java private 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; // . . . @Override public void onClick(View view) { Activity activity = getActivity(); if (activity != null) { switch (view.getId()) { case R.id.buttonAudioStart: if (!mStarted) { Intent musicIntent = new Intent(activity.getApplicationContext(), MediaPlaybackService.class); musicIntent.putExtra("URIString", mAudioFileUri.toString()); activity.startService(musicIntent); mStarted = true; } break; case R.id.buttonAudioStop: activity.stopService(new Intent(activity.getApplicationContext(), MediaPlaybackService.class)); mStarted = false; break; case R.id.buttonAudioRecord: startActivityForResult(mRecordAudioIntent, AUDIO_CAPTURED); break; case R.id.buttonAudioExit: activity.finish(); break; } } } public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK && requestCode == AUDIO_CAPTURED) { mAudioFileUri = data.getData(); } } 1 2 3
// AudioFragment. kt private val mAudioFilePath = Environment // AudioFragment.kt private val mAudioFilePath = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_MUSIC).path + File.separator + "sample_audio.mp3" private lateinit var mAudioFileUri: Uri private 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()) 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 } } } 1 2 3
Media Player States Source: https://developer.android.com/reference/android/media/MediaPlayer.html
Playing Audio: Service: Java <service android:enabled="true” android:name=".MediaPlaybackService”/> // MediaPlayerService.java public class MediaPlaybackService extends Service { MediaPlayer player; @Override public IBinder onBind(Intent intent) { return null;} @Override public void onCreate() { player = MediaPlayer.create(this, R.raw.sample_audio); player.setLooping(true); } @Override public 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; } @Override public void onDestroy() { player.stop(); } } Note use of default resource Note need to reset, set and prepare the media player in order to play a new file
Playing Audio: Service: Kotlin <service android:enabled="true” android:name=".MediaPlaybackService”/> // MediaPlayerService.kt class 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.extras if (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() } }
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>
Handling Video: Java (1) // VideoFragment.java public 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); @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Activity activity = getActivity(); View v = inflater.inflate(R.layout.fragment_video, container, false); mVideoView = v.findViewById(R.id.videoView); // Get references to Buttons and for each Button, setOnClickListener(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. if (activity != null) { String videoResourceName = "android.resource://" + activity.getPackageName() + File.separator + R.raw.sample_video; mVideoFileUri = Uri.parse(videoResourceName); } } // Guard against no video recorder app (disable the "record" button). if (activity != null) { PackageManager packageManager = activity.getPackageManager(); if (packageManager.resolveActivity(mRecordVideoIntent, PackageManager.MATCH_DEFAULT_ONLY) == null) { buttonRecord.setEnabled(false); } } return v; } }
Handling Video: Java (2) // VideoFragment.java (continued) @Override public void onClick(View view) { switch (view.getId()) { case R.id.buttonVideoStart: // Load and start the movie mVideoView.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: Activity activity = getActivity(); if (activity != null) { activity.finish(); } break; } } public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK && requestCode == VIDEO_CAPTURED) { mVideoFileUri = data.getData(); } } } Connect startActivityForResult with onActivityResult callback. How does activity know what was the calling activity?
Handling Video: Kotlin (1) // AudioFragment.kt class VideoFragment : Fragment(), View.OnClickListener { private lateinit var mButtonStart: Button private lateinit var mButtonStop: Button private lateinit var mButtonRecord: Button private lateinit var mVideoView: VideoView private var mVideoFileUri: Uri? = null private 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_audio, container, false) mVideoView = v.findViewById(R.id.videoView) // Get references to Buttons and for each Button, setOnClickListener(this); 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_video mVideoFileUri = Uri.parse(videoResourceName) } // Guard against no video recorder app (disable the "record" button). val packageManager = activity?.packageManager if (packageManager?.resolveActivity(mRecordVideoIntent, PackageManager.MATCH_DEFAULT_ONLY) == null) { mButtonRecord.isEnabled = false } return v }
Handling Video: Kotlin (2) override fun onClick(view: View) { when (view.id) { R.id.buttonVideoStart -> { // Load and start the movie mVideoView.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 } } }
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>
Handling Images: Java (1) // ImageFragment.java public 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); @Override public 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 = v.findViewById(R.id.buttonImageShow); Button buttonCapture = v.findViewById(R.id.buttonImageCapture); Button buttonExit = v.findViewById(R.id.buttonImageExit); // Set up onClickListener(this) for the buttons return v; } Need to convert the image file into a bitmap.
Handling Images: Java (2) // ImageFragment.java (continued) @Override public 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: Activity activity = getActivity(); if (activity != null) { activity.finish(); } break; } }
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); } } } Explain why the garbage collector is being called 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.kt class ImagesFragment : Fragment(), View.OnClickListener { private lateinit var imageView: ImageView private val imageFilePath = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES).path + File.separator + "other_image.png" private lateinit var imageBitmap: Bitmap private 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(R.id.imageView) val buttonShow = v.findViewById(R.id.buttonImageShow) val buttonCapture = v.findViewById(R.id.buttonImageCapture) val buttonExit = v.findViewById(R.id.buttonImageExit) // Set onClickListener(this) for each Button return v } // . . .
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 SVGs imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null) imageView.setImageResource(R.drawable.ic_scoreboard) } } R.id.buttonImageCapture -> startActivityForResult(mCaptureImageIntent, IMAGE_CAPTURED) R.id.buttonImageExit -> activity?.finish() } }
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?.extras if (extras != null) { imageBitmap = extras.get("data") as Bitmap imageView.setImageBitmap(imageBitmap) } } }
Sensors Uses: Example Tic-Tac-Toe files: Issues: 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 Type Examples Motion Accelerometer, gyroscope Environmental Light, temperature, humidity, barometric pressure Miscellaneous 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 Views
RecyclerView Workflow Source: 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: Java private RecyclerView mSensorRecyclerView; private SensorAdapter mSensorAdapter; private SensorManager mSensorManager; private List<Sensor> mSensorList; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_sensor_list, container, false); Activity activity = getActivity(); RecyclerView sensorRecyclerView = v.findViewById(R.id.sensor_recycler_view); if (activity != null) { sensorRecyclerView.setLayoutManager(new LinearLayoutManager(activity)); mSensorManager = (SensorManager) activity.getSystemService(SENSOR_SERVICE); if (mSensorManager != null) { mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); SensorAdapter adapter = new SensorAdapter(mSensorList); sensorRecyclerView.setAdapter(adapter); sensorRecyclerView.setItemAnimator(new DefaultItemAnimator()); } } return v; }
Listing Available Sensors: Kotlin private lateinit var mSensorRecyclerView: RecyclerView private lateinit var mAdapter: SensorAdapter private lateinit var mSensorManager: SensorManager private 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(R.id.sensor_recycler_view) mSensorRecyclerView.layoutManager = LinearLayoutManager(activity) mSensorManager = activity?.getSystemService(SENSOR_SERVICE) as SensorManager mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL) mAdapter = SensorAdapter(mSensorList) mSensorRecyclerView.adapter = mAdapter mSensorRecyclerView.itemAnimator = DefaultItemAnimator() return v }
Sensor Holder Java Kotlin private class SensorHolder extends RecyclerView.ViewHolder { private Sensor mSensor; private String mDescriptionStr; private TextView mSensorInfoTextView; public SensorHolder(LayoutInflater inflater, ViewGroup parent) { super(inflater.inflate( R.layout.list_item_sensor, parent, false)); mSensorInfoTextView = itemView.findViewById(R.id.sensor_data); } public void bind(Sensor sensor) { mSensor = sensor; mDescriptionStr = getSensorDescription( sensor); mSensorInfoTextView.setText( mDescriptionStr); } } private inner class SensorHolder( inflater: LayoutInflater, parent: ViewGroup) : RecyclerView.ViewHolder( inflater.inflate(R.layout.list_item_sensor, parent, false)) { private lateinit var mSensor: Sensor private lateinit var mDescriptionStr: String private val mSensorInfoTextView: TextView init { mSensorInfoTextView = itemView.findViewById(R.id.sensor_data) } fun bind(sensor: Sensor) { mSensor = sensor mDescriptionStr = getSensorDescription( sensor) mSensorInfoTextView.text = mDescriptionStr } }
Sensor Adapter Java Kotlin 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); } @Override public void onBindViewHolder(SensorHolder holder, int position) { Sensor sensor = SensorsFragment.this.mSensorList .get(position); String sensorDescription = getSensorDescription( sensor); holder.bind(sensor); } @Override public int getItemCount() { return mSensorList.size(); } } private 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 } }
Registering Sensor Updates Java Kotlin @Override public void onResume() { super.onResume(); // . . . // Start listening to sensor updates for (Sensor sensor : mSensorList) { mSensorManager.registerListener( this, sensor, SensorManager.SENSOR_DELAY_NORMAL); } } // . . . @Override public void onPause() { super.onPause(); // Stop updates when paused mSensorManager.unregisterListener(this); } override fun onResume() { super.onResume() // . . . // Start listening to sensor updates for (sensor in mSensorList) { mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL) } } // . . . override fun onPause() { super.onPause() // Stop updates when paused mSensorManager.unregisterListener(this) }
Receiving Sensor Updates Java Kotlin @Override public 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); } override 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) } See complete method for how to filter out noise.
Extracting Sensor Parameters Java Kotlin public String getSensorDescription( Sensor sensor) { return "Sensor: " + sensor.getName() + "; Ver :" + sensor.getVersion() + "; Range: " + sensor.getMaximumRange() + "; Power: " + sensor.getPower() + "; Res: " + sensor.getResolution(); } fun getSensorDescription( sensor: Sensor): String { return "Sensor: " + sensor.name + "; Ver :" + sensor.version + "; Range: " + sensor.maximumRange + "; Power: " + sensor.power + "; Res: " + sensor.resolution }
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
Questions and comments? Thank You Questions and comments?