Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» CS 491 Lecture 11 – Lonely Phone

teaching machines

CS 491 Lecture 11 – Lonely Phone

October 11, 2011 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
cs491 mobile, fall 2011, lectures.

Agenda

Goal

Our goal today is to write an app I call Lonely Phone. It’s a prank app that you should install on other people’s phones. It’s functionality is to beep whenever the phone is set down flat but stop when you pick it up. Annoyance is the secondary goal; encouraging people to lock their phones is the primary.

Android Components

We’ve met Activity, which is only one of the four components that make up our apps. In short, the components are:

Today we’ll explore Service and BroadcastListener in more detail.

Processes

All components of an app run in a single process. This process, once started, will persist until the OS decides to stop it. The priority of which processes are retained fall in this spectrum, from most important to least:

  1. Foreground: processes required for what the user is actively doing; includes running Activity, a foreground Service, a service bound to a running Activity, a Service running a callback, a BroadcastReceiver catching a signal.
  2. Visible: processes affecting UI; includes paused but not stopped Activity and Service bound to such an Activity.
  3. Service: processes not in 1 or 2 but explicitly marked as needing longevity; includes Services started with startService (not bindService).
  4. Background: processes with no visible components; including stopped (but not destroyed) Activitys. These aren’t immediately killed in the event that they will soon be restored.
  5. Empty: processes with no active components; including destroyed Activitys.

Services

One of the compelling features of a Service is that it is “alive” even when not focused. If you’ve got a long-running operation that you need to perform that is not tied to the user interface, then a Service is the way to go. You might think a thread in your Activity is sufficient to perform background tasks, but if your Activity is paused, it and the containing process may be destroyed to free resources for other apps. A thread tied to a Service is less likely to get killed. The service falls into priority level 3, while the paused Activity is in priority level 4.

There are a number of system services available to us. Like the SENSOR_SERVICE, which lets us register listeners for certain sensor events. Today, we want to detect a horizontal phone, so we need a couple of sensor events. Well, really, we want to ask the phone its orientation. SensorManager.getOrientation can do that for us. It’ll give you yaw, pitch, and roll, but you need to give it a rotation matrix. SensorManager.getRotationMatrix will give you the matrix, but you need to give it some readings from two sensors: the accelerometer and the magnetic field sensors. Don’t ask me how these sensors work. We can get their values by registering a listener with the sensor service:

final SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensorManager.registerListener(LonelyActivity.this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_GAME);
sensorManager.registerListener(LonelyActivity.this, sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_GAME);

Where do we register this? In an Activity, we don’t really want to listen for events when the Activity is paused. That’ll drain the battery. So, we should start it up in onResume and cancel it in onPause. In a Service, we only have a couple lifecycle callbacks. We’re going to drain the battery. Serves ’em right for leaving their phone unlocked.

Our callback can get the yaw, pitch, and roll like so:

@Override
public void onSensorChanged(SensorEvent event) {
  if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
    mags = event.values;
  } else if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
    accels = event.values;
  }

  if (accels != null && mags != null) {
    SensorManager.getRotationMatrix(rotation, inclination, accels, mags);
    SensorManager.getOrientation(rotation, ypr);
    ((TextView) findViewById(R.id.ypr)).setText(String.format("%f\n%f\n%f", ypr[0], ypr[1], ypr[2]));
  }
}

Let’s add a couple of features to make development of this app easier: a) let’s set the background color of our test Activity to red when the phone is flat, and b) let’s add a SeekBar to adjust the sensitivity of “flatness.”

LonelyService

Okay, we’ve done some testing with a plain old Activity. Let’s now move our code to a Service, so that folks who get this installed on their phone will not have a UI to fiddle with and turn beeping off. We need an onCreate and an onDestroy, which turn on and off SensorEvent listening. In our listener, we want to recognize two significant events: a phone goes flat and a phone goes unflat. In between these two events, we want the phone to beep. Here’s our TODO list to make this work right:

We can determine the prior state by looking to the handler and seeing if it has a task it’s running. If so, the phone was flat. Here’s one go at this:

if (task == null && Math.abs(ypr[1]) < sensitivity && Math.abs(ypr[2]) < sensitivity) {
  player = MediaPlayer.create(this, R.raw.beep);
  task = new Runnable() {
    @Override
    public void run() {
      player.seekTo(0);
      player.start();
      handler.postDelayed(this, PAUSE);
    }
  };
  handler.post(task);
} else if (task != null && (Math.abs(ypr[1]) > sensitivity || Math.abs(ypr[2]) > sensitivity)) {
  handler.removeCallbacks(task);
  player.release();
  task = null;
}

Starting a service

Before a service can be run, it must be declared in the manifest:

<service android:name=".LonelyService"></service>

It’d be swell if we could start the service immediately upon installation. Unfortunately, I don’t know of a way to do so. We can trigger it to run at boot time, and we can also have an Activity turn it on. There are two ways to do this:

  1. Using bindService, which pairs the Activity and Service for interaction.
  2. Using startService, which spawns the service for an independent task. A Service started with startService has less priority than a bound one, unless it’s explicitly marked really super-duper important with startForeground.

Stopping a service is important if you’re going to be writing an annoying prank application. If we bind, we must unbind. If we start, we must stop.

Communication between Activity and Service

It’d be nice to set the sensitivity of flatness. Let’s add a SeekBar to do just that.

In its listener, we get the new values. But this is UI stuff. The Service needs it. How do get it? Through a Binder. If we bind the service, a binder is given to us, we lets us call communicate with the Service through an interface it publishes.

Binders take on two forms. If other apps can make use of our service, we’ll need to define the interface in this language called AIDL. For app-local services, we can have the Binder return the service:

public class LocalBinder extends Binder {
  LonelyService getService() {
    return LonelyService.this;
  }
}

Then in onBind, we can return an instance:

@Override
public IBinder onBind(Intent intent) {
  return binder;
}

Back in the Activity, we bind the service:

bindService(new Intent(LonelyActivity.this, LonelyService.class), connection, 0);

That connection variable is where we get the Binder from whom we can retrieve the Service. It has two callbacks:

private ServiceConnection connection = new ServiceConnection() {
  @Override
  public void onServiceDisconnected(ComponentName name) {
    service = null;
  }

  @Override
  public void onServiceConnected(ComponentName name,
                                 IBinder binder) {
    service = ((LonelyService.LocalBinder) binder).getService();
  }
};

Now, we can do service-y things to our service. Like set the sensitivity.

Starting a Service at boot

The third component is BroadcastReceiver. These are short-lived handlers of system signals. A number of standard signals are defined, but custom ones can be added. As there’s no lifecycle to a BroadcastReceiver, our subclass is short and sweet:

public class LonelyReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context,
                        Intent intent) {
    if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
      context.startService(new Intent(context, LonelyService.class));
    }
  }
}

We have to add an intent-filter when we declare our receiver in the manifest:

<receiver android:name=".LonelyReceiver">
  <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
  </intent-filter>
</receiver>

Starting things up on boot is a great example of the tragedy of the commons. If everybody did it, more phones would end up smashed on the sidewalk in frustration. We want fast boot times. Accordingly, we have to declare our bootiness in the manifest:

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

And there we have it.