From 434d41c45df023fd543a94e88b0bda2dc5c7844d Mon Sep 17 00:00:00 2001 From: Trevor Johns Date: Fri, 8 May 2015 19:29:42 -0700 Subject: [PATCH] Update samples prebuilts for lmp-mr1-ub-docs Synced to developers/samples/android commit 54bab34b386e343e9d0ea75a5fb8d13db2c71eb5. Change-Id: I1c9d9d2c1f53a051d7b4d85303d5c01ab6f16e68 --- .../browseable/AlwaysOn/AndroidManifest.xml | 45 +++ samples/browseable/AlwaysOn/_index.jd | 9 + .../AlwaysOn/res/layout/activity_main.xml | 28 ++ .../res/layout/rect_activity_main.xml | 59 ++++ .../res/layout/round_activity_main.xml | 59 ++++ .../browseable/AlwaysOn/res/values/dimens.xml | 23 ++ .../AlwaysOn/res/values/strings.xml | 23 ++ .../MainActivity.java | 332 ++++++++++++++++++ .../AndroidManifest.xml | 39 ++ .../BluetoothAdvertisements/_index.jd | 10 + .../res/drawable-hdpi/ic_action_refresh.png | Bin 0 -> 663 bytes .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4199 bytes .../res/drawable-hdpi/tile.9.png | Bin 0 -> 196 bytes .../res/drawable-mdpi/ic_action_refresh.png | Bin 0 -> 508 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2535 bytes .../res/drawable-xhdpi/ic_action_refresh.png | Bin 0 -> 895 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 6022 bytes .../res/drawable-xxhdpi/ic_action_refresh.png | Bin 0 -> 1239 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 11040 bytes .../res/layout/activity_main.xml | 55 +++ .../res/layout/fragment_advertiser.xml | 52 +++ .../res/layout/listitem_scanresult.xml | 42 +++ .../res/menu-v21/scanner_menu.xml | 8 + .../res/values-sw600dp/template-dimens.xml | 24 ++ .../res/values-sw600dp/template-styles.xml | 25 ++ .../res/values-v11/template-styles.xml | 22 ++ .../res/values-v21/base-colors.xml | 23 ++ .../res/values-v21/base-template-styles.xml | 26 ++ .../res/values/base-strings.xml | 29 ++ .../res/values/strings.xml | 30 ++ .../res/values/template-dimens.xml | 32 ++ .../res/values/template-styles.xml | 42 +++ .../AdvertiserFragment.java | 190 ++++++++++ .../Constants.java | 22 ++ .../MainActivity.java | 130 +++++++ .../ScanResultAdapter.java | 147 ++++++++ .../ScannerFragment.java | 212 +++++++++++ .../MainActivity.java | 8 +- .../Application/AndroidManifest.xml | 4 + .../Application/res/values/colors.xml | 4 - .../config/GlideConfiguration.java | 42 +++ .../ui/AttractionListActivity.java | 6 +- .../ui/DetailActivity.java | 4 +- .../res/values/colors.xml} | 11 +- .../drawable-hdpi/ic_full_open_on_device.png | Bin 3073 -> 0 bytes .../res/drawable-hdpi/ic_full_openonphone.png | Bin 0 -> 1554 bytes .../drawable-xhdpi/ic_full_open_on_device.png | Bin 1841 -> 0 bytes .../drawable-xhdpi/ic_full_openonphone.png | Bin 0 -> 15270 bytes .../Wearable/res/layout/activity_main.xml | 3 - .../Wearable/res/layout/gridpager_action.xml | 11 +- .../res/layout/gridpager_action_round.xml | 59 ---- .../res/layout/gridpager_action_square.xml | 63 ---- .../Wearable/res/values/styles.xml | 14 - .../ui/AttractionsActivity.java | 4 +- .../ui/AttractionsGridPagerAdapter.java | 53 +-- 55 files changed, 1823 insertions(+), 201 deletions(-) create mode 100644 samples/browseable/AlwaysOn/AndroidManifest.xml create mode 100644 samples/browseable/AlwaysOn/_index.jd create mode 100644 samples/browseable/AlwaysOn/res/layout/activity_main.xml create mode 100644 samples/browseable/AlwaysOn/res/layout/rect_activity_main.xml create mode 100644 samples/browseable/AlwaysOn/res/layout/round_activity_main.xml create mode 100644 samples/browseable/AlwaysOn/res/values/dimens.xml create mode 100644 samples/browseable/AlwaysOn/res/values/strings.xml create mode 100644 samples/browseable/AlwaysOn/src/com.example.android.wearable.wear.alwayson/MainActivity.java create mode 100644 samples/browseable/BluetoothAdvertisements/AndroidManifest.xml create mode 100644 samples/browseable/BluetoothAdvertisements/_index.jd create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_action_refresh.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/tile.9.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_action_refresh.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_action_refresh.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_action_refresh.png create mode 100644 samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png create mode 100755 samples/browseable/BluetoothAdvertisements/res/layout/activity_main.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/layout/fragment_advertiser.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/layout/listitem_scanresult.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/menu-v21/scanner_menu.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-dimens.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-styles.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values-v11/template-styles.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values-v21/base-colors.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values-v21/base-template-styles.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values/base-strings.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values/strings.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values/template-dimens.xml create mode 100644 samples/browseable/BluetoothAdvertisements/res/values/template-styles.xml create mode 100644 samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java create mode 100644 samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/Constants.java create mode 100644 samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java create mode 100644 samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java create mode 100644 samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java create mode 100644 samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/config/GlideConfiguration.java rename samples/browseable/XYZTouristAttractions/{Wearable/res/color/action_color.xml => Shared/res/values/colors.xml} (73%) delete mode 100644 samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_open_on_device.png create mode 100644 samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_openonphone.png delete mode 100644 samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_open_on_device.png create mode 100644 samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_openonphone.png delete mode 100644 samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_round.xml delete mode 100644 samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_square.xml diff --git a/samples/browseable/AlwaysOn/AndroidManifest.xml b/samples/browseable/AlwaysOn/AndroidManifest.xml new file mode 100644 index 000000000..c0fce9f27 --- /dev/null +++ b/samples/browseable/AlwaysOn/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/AlwaysOn/_index.jd b/samples/browseable/AlwaysOn/_index.jd new file mode 100644 index 000000000..b6057821b --- /dev/null +++ b/samples/browseable/AlwaysOn/_index.jd @@ -0,0 +1,9 @@ +page.tags="AlwaysOn" +sample.group=Wearable +@jd:body + +

+ + Demonstrates a native Android Wear app using ambient screen support. + > +

diff --git a/samples/browseable/AlwaysOn/res/layout/activity_main.xml b/samples/browseable/AlwaysOn/res/layout/activity_main.xml new file mode 100644 index 000000000..d808e6bbb --- /dev/null +++ b/samples/browseable/AlwaysOn/res/layout/activity_main.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/samples/browseable/AlwaysOn/res/layout/rect_activity_main.xml b/samples/browseable/AlwaysOn/res/layout/rect_activity_main.xml new file mode 100644 index 000000000..bfb814770 --- /dev/null +++ b/samples/browseable/AlwaysOn/res/layout/rect_activity_main.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + diff --git a/samples/browseable/AlwaysOn/res/layout/round_activity_main.xml b/samples/browseable/AlwaysOn/res/layout/round_activity_main.xml new file mode 100644 index 000000000..8fa7a2df4 --- /dev/null +++ b/samples/browseable/AlwaysOn/res/layout/round_activity_main.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + diff --git a/samples/browseable/AlwaysOn/res/values/dimens.xml b/samples/browseable/AlwaysOn/res/values/dimens.xml new file mode 100644 index 000000000..d44096abc --- /dev/null +++ b/samples/browseable/AlwaysOn/res/values/dimens.xml @@ -0,0 +1,23 @@ + + + + + 24dp + 16dp + + 34dp + 34dp + diff --git a/samples/browseable/AlwaysOn/res/values/strings.xml b/samples/browseable/AlwaysOn/res/values/strings.xml new file mode 100644 index 000000000..7d4c2f63f --- /dev/null +++ b/samples/browseable/AlwaysOn/res/values/strings.xml @@ -0,0 +1,23 @@ + + + + Always On Example + Timestamp: %1$d + Active Mode (Handler) + Ambient Mode (Alarm) + Update rate: %1$d sec + Draw count: %1$d + diff --git a/samples/browseable/AlwaysOn/src/com.example.android.wearable.wear.alwayson/MainActivity.java b/samples/browseable/AlwaysOn/src/com.example.android.wearable.wear.alwayson/MainActivity.java new file mode 100644 index 000000000..51f287d81 --- /dev/null +++ b/samples/browseable/AlwaysOn/src/com.example.android.wearable.wear.alwayson/MainActivity.java @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.wearable.wear.alwayson; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.support.wearable.activity.WearableActivity; +import android.support.wearable.view.WatchViewStub; +import android.util.Log; +import android.widget.TextView; + +import java.lang.ref.WeakReference; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +/** + * Demonstrates support for Ambient screens by extending WearableActivity and overriding + * onEnterAmbient, onUpdateAmbient, and onExitAmbient. + * + * There are two modes (Active and Ambient). To trigger future updates (data/screen), we use a + * custom Handler for the "Active" mode and an Alarm for the "Ambient" mode. + * + * Why don't we use just one? Handlers are generally less battery intensive and can be triggered + * every second. However, they can not wake up the processor (common in Ambient mode). + * + * Alarms can wake up the processor (what we need for Ambient), but they struggle with quick updates + * (less than one second) and are much less efficient compared to Handlers. + * + * Therefore, we use Handlers for "Active" mode (can trigger every second and are better on the + * battery), and we use Alarms for "Ambient" mode (only need to update once every 20 seconds and + * they can wake up a sleeping processor). + * + * Again, the Activity waits 20 seconds between doing any processing (getting data, updating screen + * etc.) while in ambient mode to conserving battery life (processor allowed to sleep). If you can + * hold off on updates for a full minute, you can throw away all the Alarm code and just use + * onUpdateAmbient() to save even more battery life. + * + * As always, you will still want to apply the performance guidelines outlined in the Watch Faces + * documention to your app. + * + * Finally, in ambient mode, this Activity follows the same best practices outlined in the + * Watch Faces API documentation, e.g., keep most pixels black, avoid large blocks of white pixels, + * use only black and white, and disable anti-aliasing. + * + */ +public class MainActivity extends WearableActivity { + + private static final String TAG = "MainActivity"; + + /** Custom 'what' for Message sent to Handler. */ + private static final int MSG_UPDATE_SCREEN = 0; + + /** Milliseconds between updates based on state. */ + private static final long ACTIVE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1); + private static final long AMBIENT_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20); + + /** Tracks latest ambient details, such as burnin offsets, etc. */ + private Bundle mAmbientDetails; + + private TextView mTimeTextView; + private TextView mTimeStampTextView; + private TextView mStateTextView; + private TextView mUpdateRateTextView; + private TextView mDrawCountTextView; + + private final SimpleDateFormat sDateFormat = + new SimpleDateFormat("HH:mm:ss", Locale.US); + + private volatile int mDrawCount = 0; + + + /** + * Since the handler (used in active mode) can't wake up the processor when the device is in + * ambient mode and undocked, we use an Alarm to cover ambient mode updates when we need them + * more frequently than every minute. Remember, if getting updates once a minute in ambient + * mode is enough, you can do away with the Alarm code and just rely on the onUpdateAmbient() + * callback. + */ + private AlarmManager mAmbientStateAlarmManager; + private PendingIntent mAmbientStatePendingIntent; + + /** + * This custom handler is used for updates in "Active" mode. We use a separate static class to + * help us avoid memory leaks. + */ + private final Handler mActiveModeUpdateHandler = new UpdateHandler(this); + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate()"); + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_main); + + setAmbientEnabled(); + + mAmbientStateAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + Intent ambientStateIntent = new Intent(getApplicationContext(), MainActivity.class); + + mAmbientStatePendingIntent = PendingIntent.getActivity( + getApplicationContext(), + 0 /* requestCode */, + ambientStateIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + + /** Determines whether watch is round or square and applies proper view. **/ + final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub); + stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() { + @Override + public void onLayoutInflated(WatchViewStub stub) { + + mTimeTextView = (TextView) stub.findViewById(R.id.time); + mTimeStampTextView = (TextView) stub.findViewById(R.id.time_stamp); + mStateTextView = (TextView) stub.findViewById(R.id.state); + mUpdateRateTextView = (TextView) stub.findViewById(R.id.update_rate); + mDrawCountTextView = (TextView) stub.findViewById(R.id.draw_count); + + refreshDisplayAndSetNextUpdate(); + } + }); + } + + /** + * This is mostly triggered by the Alarms we set in Ambient mode and informs us we need to + * update the screen (and process any data). + */ + @Override + public void onNewIntent(Intent intent) { + Log.d(TAG, "onNewIntent(): " + intent); + super.onNewIntent(intent); + + setIntent(intent); + + refreshDisplayAndSetNextUpdate(); + } + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy()"); + + mActiveModeUpdateHandler.removeMessages(MSG_UPDATE_SCREEN); + mAmbientStateAlarmManager.cancel(mAmbientStatePendingIntent); + + super.onDestroy(); + } + + /** + * Prepares UI for Ambient view. + */ + @Override + public void onEnterAmbient(Bundle ambientDetails) { + Log.d(TAG, "onEnterAmbient()"); + super.onEnterAmbient(ambientDetails); + + /** + * In this sample, we aren't using the ambient details bundle (EXTRA_BURN_IN_PROTECTION or + * EXTRA_LOWBIT_AMBIENT), but if you need them, you can pull them from the local variable + * set here. + */ + mAmbientDetails = ambientDetails; + + /** Clears Handler queue (only needed for updates in active mode). */ + mActiveModeUpdateHandler.removeMessages(MSG_UPDATE_SCREEN); + + /** + * Following best practices outlined in WatchFaces API (keeping most pixels black, + * avoiding large blocks of white pixels, using only black and white, + * and disabling anti-aliasing anti-aliasing, etc.) + */ + mStateTextView.setTextColor(Color.WHITE); + mUpdateRateTextView.setTextColor(Color.WHITE); + mDrawCountTextView.setTextColor(Color.WHITE); + + mTimeTextView.getPaint().setAntiAlias(false); + mTimeStampTextView.getPaint().setAntiAlias(false); + mStateTextView.getPaint().setAntiAlias(false); + mUpdateRateTextView.getPaint().setAntiAlias(false); + mDrawCountTextView.getPaint().setAntiAlias(false); + + refreshDisplayAndSetNextUpdate(); + } + + /** + * Updates UI in Ambient view (once a minute). Because we need to update UI sooner than that + * (every ~20 seconds), we also use an Alarm. However, since the processor is awake for this + * callback, we might as well call refreshDisplayAndSetNextUpdate() to update screen and reset + * the Alarm. + * + * If you are happy with just updating the screen once a minute in Ambient Mode (which will be + * the case a majority of the time), then you can just use this method and remove all + * references/code regarding Alarms. + */ + @Override + public void onUpdateAmbient() { + Log.d(TAG, "onUpdateAmbient()"); + super.onUpdateAmbient(); + + refreshDisplayAndSetNextUpdate(); + } + + /** + * Prepares UI for Active view (non-Ambient). + */ + @Override + public void onExitAmbient() { + Log.d(TAG, "onExitAmbient()"); + super.onExitAmbient(); + + /** Clears out Alarms since they are only used in ambient mode. */ + mAmbientStateAlarmManager.cancel(mAmbientStatePendingIntent); + + mStateTextView.setTextColor(Color.GREEN); + mUpdateRateTextView.setTextColor(Color.GREEN); + mDrawCountTextView.setTextColor(Color.GREEN); + + mTimeTextView.getPaint().setAntiAlias(true); + mTimeStampTextView.getPaint().setAntiAlias(true); + mStateTextView.getPaint().setAntiAlias(true); + mUpdateRateTextView.getPaint().setAntiAlias(true); + mDrawCountTextView.getPaint().setAntiAlias(true); + + refreshDisplayAndSetNextUpdate(); + } + + /** + * Loads data/updates screen (via method), but most importantly, sets up the next refresh + * (active mode = Handler and ambient mode = Alarm). + */ + private void refreshDisplayAndSetNextUpdate() { + + loadDataAndUpdateScreen(); + + long timeMs = System.currentTimeMillis(); + + if (isAmbient()) { + /** Prevents time drift while calculating trigger time (based on state). */ + long delayMs = AMBIENT_INTERVAL_MS - (timeMs % AMBIENT_INTERVAL_MS); + long triggerTimeMs = timeMs + delayMs; + + mAmbientStateAlarmManager.cancel(mAmbientStatePendingIntent); + mAmbientStateAlarmManager.setExact( + AlarmManager.RTC_WAKEUP, + triggerTimeMs, + mAmbientStatePendingIntent); + + } else { + /** Prevents time drift. */ + long delayMs = ACTIVE_INTERVAL_MS - (timeMs % ACTIVE_INTERVAL_MS); + + mActiveModeUpdateHandler.removeMessages(MSG_UPDATE_SCREEN); + mActiveModeUpdateHandler.sendEmptyMessageDelayed(MSG_UPDATE_SCREEN, delayMs); + } + } + + /** + * Updates display based on Ambient state. If you need to pull data, you should do it here. + */ + private void loadDataAndUpdateScreen() { + + mDrawCount += 1; + long currentTimeMs = System.currentTimeMillis(); + Log.d(TAG, "loadDataAndUpdateScreen(): " + currentTimeMs + "(" + isAmbient() + ")"); + + if (isAmbient()) { + + mTimeTextView.setText(sDateFormat.format(new Date())); + mTimeStampTextView.setText(getString(R.string.timestamp_label, currentTimeMs)); + + mStateTextView.setText(getString(R.string.mode_ambient_label)); + mUpdateRateTextView.setText( + getString(R.string.update_rate_label, (AMBIENT_INTERVAL_MS / 1000))); + + mDrawCountTextView.setText(getString(R.string.draw_count_label, mDrawCount)); + + } else { + mTimeTextView.setText(sDateFormat.format(new Date())); + mTimeStampTextView.setText(getString(R.string.timestamp_label, currentTimeMs)); + + mStateTextView.setText(getString(R.string.mode_active_label)); + mUpdateRateTextView.setText( + getString(R.string.update_rate_label, (ACTIVE_INTERVAL_MS / 1000))); + + mDrawCountTextView.setText(getString(R.string.draw_count_label, mDrawCount)); + } + } + + /** + * Handler separated into static class to avoid memory leaks. + */ + private static class UpdateHandler extends Handler { + private final WeakReference mMainActivityWeakReference; + + public UpdateHandler(MainActivity reference) { + mMainActivityWeakReference = new WeakReference(reference); + } + + @Override + public void handleMessage(Message message) { + MainActivity mainActivity = mMainActivityWeakReference.get(); + + if (mainActivity != null) { + switch (message.what) { + case MSG_UPDATE_SCREEN: + mainActivity.refreshDisplayAndSetNextUpdate(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml b/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml new file mode 100644 index 000000000..48084fc97 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/BluetoothAdvertisements/_index.jd b/samples/browseable/BluetoothAdvertisements/_index.jd new file mode 100644 index 000000000..27296b0ce --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/_index.jd @@ -0,0 +1,10 @@ +page.tags="BluetoothAdvertisements" +sample.group=Connectivity +@jd:body + +

+ +This samples demonstrates how to use the Bluetooth Low Power Advertisements API +with a single device acting as both scanner and advertiser. + +

diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_action_refresh.png b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_action_refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..dae27903e9ba3415808d48e6ac20afd6de64907b GIT binary patch literal 663 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 literal 0 HcmV?d00001 diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/tile.9.png b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/tile.9.png new file mode 100644 index 0000000000000000000000000000000000000000..135862883e26eddce2b19db021adf62e10357ad0 GIT binary patch literal 196 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 VDq)Usy-@%F002ovPDHLkV1gKjk--1} literal 0 HcmV?d00001 diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5d07b3f06606f5f005afa8237a53f7ff3ef5966e GIT binary patch literal 6022 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 ABme*a literal 0 HcmV?d00001 diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_action_refresh.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_action_refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..44ee117ee91541ed71f27685dde0b03b51b89f7f GIT binary patch literal 1239 zcmV;|1StE7P) z5dr`J0000000000003wMK0iOlU?Cj&OWC$yA{_Z2)WC*+^3#HR9|H6uA>~fK3{<1{ zx9TJMcVE5FWjmGLALzB$>t!`#{^HAzC-VQuuTB6-#z?*lWgC?UZweA&FWb8$^H76) z!mN&nQ463fJ(caDWZAAk*8D0--5QX89Reugr;2!Apm6o7yyK}t@QoB;(@6m&@zm+L zXX5LERe&ULj9)N@8dqSwza)^=FVx@~0|uQDK$1_LS#ozOCiFa$|7_vXxk@E}=lGX$ zb9tSisuhN+*`}~Zd988O0$7lrt4w7tNjO<#STx9wn~EW?eIKm=4av_`_K`WdhrYUp zLE8jKT1Gxu{Fb+Mkv?Z6e-TR| zcH_A714GMutBO%J^{XIKHqUZ+Pi82)Wa2B=Cc9ad>)`Hx-zc06bN9JrtLsAQx;^H{e0&r zy5Iq3)}MiVUIBD7p2&PcdjM$mQwLna5j08F}_ENY*|b4D(*pbw1z z_Z=a7T`}HKqA_b@2$k5EUi_T?zSj zn$Hix1h^9DLeL2U@IqJZH;w{5aBE-d2#eWXcz~Y#k!}FCneBxDdguhOnrwss)z(i1 zm~Nrj9s=+oHTVuZfYJJO0_<#`_CbKlpoM0-IUwuvgph`ww|<&_elf^?tanc)9y;^2 zh#6E^R$o+ahWO`r8jf|QD)cN3zUpM5nU7tPZ^Hd_E>)g0ru_v=gUrX--eDhOfa}S+ z6C?SlFx%g-balESWzp8qF%rjSyd#J{w&Cwng{~| z2mtZ}3D5`m;YApM1poj50000000000fS~v#zyO={=$CR|bEp6S002ovPDHLkV1lVS BMRNcE literal 0 HcmV?d00001 diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef21e1f4dfe8eeec63fde13a453812511646c83 GIT binary patch literal 11040 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 WRh@=GnE09b3XqXd5U&x{5BMKGuL(l{ literal 0 HcmV?d00001 diff --git a/samples/browseable/BluetoothAdvertisements/res/layout/activity_main.xml b/samples/browseable/BluetoothAdvertisements/res/layout/activity_main.xml new file mode 100755 index 000000000..817cccc48 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/layout/activity_main.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/browseable/BluetoothAdvertisements/res/layout/fragment_advertiser.xml b/samples/browseable/BluetoothAdvertisements/res/layout/fragment_advertiser.xml new file mode 100644 index 000000000..4031b8d7e --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/layout/fragment_advertiser.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + diff --git a/samples/browseable/BluetoothAdvertisements/res/layout/listitem_scanresult.xml b/samples/browseable/BluetoothAdvertisements/res/layout/listitem_scanresult.xml new file mode 100644 index 000000000..ff5956fbc --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/layout/listitem_scanresult.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/res/menu-v21/scanner_menu.xml b/samples/browseable/BluetoothAdvertisements/res/menu-v21/scanner_menu.xml new file mode 100644 index 000000000..8dda284cb --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/menu-v21/scanner_menu.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-dimens.xml b/samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-dimens.xml @@ -0,0 +1,24 @@ + + + + + + + @dimen/margin_huge + @dimen/margin_medium + + diff --git a/samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-styles.xml b/samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values-sw600dp/template-styles.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/samples/browseable/BluetoothAdvertisements/res/values-v11/template-styles.xml b/samples/browseable/BluetoothAdvertisements/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/samples/browseable/BluetoothAdvertisements/res/values/base-strings.xml b/samples/browseable/BluetoothAdvertisements/res/values/base-strings.xml new file mode 100644 index 000000000..6bfd4139b --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values/base-strings.xml @@ -0,0 +1,29 @@ + + + + BluetoothAdvertisements + + + + diff --git a/samples/browseable/BluetoothAdvertisements/res/values/strings.xml b/samples/browseable/BluetoothAdvertisements/res/values/strings.xml new file mode 100644 index 000000000..197178d00 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values/strings.xml @@ -0,0 +1,30 @@ + + + User declined to enable Bluetooth, exiting Bluetooth Advertisements. + Nearby Devices + Broadcast Device + + Bluetooth is not supported on this device. + Bluetooth Advertisements are not supported on this device. + + Refresh + Start Advertising failed: + already started. + data packet exceeded 31 Byte limit. + not supported on this device. + internal error. + too many advertisers. + Error: Bluetooth object null + Last Seen: + just now + minute ago + hour ago + seconds ago + minutes ago + hours ago + No devices found - refresh to try again. + seconds. + Scanning for + Scanning already started. + + \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/res/values/template-dimens.xml b/samples/browseable/BluetoothAdvertisements/res/values/template-dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values/template-dimens.xml @@ -0,0 +1,32 @@ + + + + + + + 4dp + 8dp + 16dp + 32dp + 64dp + + + + @dimen/margin_medium + @dimen/margin_medium + + diff --git a/samples/browseable/BluetoothAdvertisements/res/values/template-styles.xml b/samples/browseable/BluetoothAdvertisements/res/values/template-styles.xml new file mode 100644 index 000000000..6e7d593dd --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values/template-styles.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java new file mode 100644 index 000000000..f8daefb04 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java @@ -0,0 +1,190 @@ +package com.example.android.bluetoothadvertisements; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.le.AdvertiseCallback; +import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertiseSettings; +import android.bluetooth.le.BluetoothLeAdvertiser; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; +import android.widget.Toast; + +/** + * Allows user to start & stop Bluetooth LE Advertising of their device. + */ +public class AdvertiserFragment extends Fragment { + + private BluetoothAdapter mBluetoothAdapter; + + private BluetoothLeAdvertiser mBluetoothLeAdvertiser; + + private AdvertiseCallback mAdvertiseCallback; + + private Switch mSwitch; + + /** + * Must be called after object creation by MainActivity. + * + * @param btAdapter the local BluetoothAdapter + */ + public void setBluetoothAdapter(BluetoothAdapter btAdapter) { + this.mBluetoothAdapter = btAdapter; + mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View view = inflater.inflate(R.layout.fragment_advertiser, container, false); + + mSwitch = (Switch) view.findViewById(R.id.advertise_switch); + mSwitch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onSwitchClicked(v); + } + }); + + return view; + } + + @Override + public void onStop() { + super.onStop(); + + if(mAdvertiseCallback != null){ + stopAdvertising(); + } + } + + /** + * Called when switch is toggled - starts or stops advertising. + * + * @param view is the Switch View object + */ + public void onSwitchClicked(View view) { + + // Is the toggle on? + boolean on = ((Switch) view).isChecked(); + + if (on) { + startAdvertising(); + } else { + stopAdvertising(); + } + } + + /** + * Starts BLE Advertising. + */ + private void startAdvertising() { + + mAdvertiseCallback = new SampleAdvertiseCallback(); + + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.startAdvertising(buildAdvertiseSettings(), buildAdvertiseData(), + mAdvertiseCallback); + } else { + mSwitch.setChecked(false); + Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show(); + } + } + + /** + * Stops BLE Advertising. + */ + private void stopAdvertising() { + + if (mBluetoothLeAdvertiser != null) { + + mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback); + mAdvertiseCallback = null; + + } else { + mSwitch.setChecked(false); + Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show(); + } + } + + /** + * Returns an AdvertiseData object which includes the Service UUID and Device Name. + */ + private AdvertiseData buildAdvertiseData() { + + // Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements. + // This includes everything put into AdvertiseData including UUIDs, device info, & + // arbitrary service or manufacturer data. + // Attempting to send packets over this limit will result in a failure with error code + // AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the + // onStartFailure() method of an AdvertiseCallback implementation. + + AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder(); + dataBuilder.addServiceUuid(Constants.Service_UUID); + dataBuilder.setIncludeDeviceName(true); + + return dataBuilder.build(); + } + + /** + * Returns an AdvertiseSettings object set to use low power (to help preserve battery life). + */ + private AdvertiseSettings buildAdvertiseSettings() { + AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder(); + settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER); + + return settingsBuilder.build(); + } + + /** + * Custom callback after Advertising succeeds or fails to start. + */ + private class SampleAdvertiseCallback extends AdvertiseCallback { + + @Override + public void onStartFailure(int errorCode) { + super.onStartFailure(errorCode); + + mSwitch.setChecked(false); + + String errorMessage = getString(R.string.start_error_prefix); + switch (errorCode) { + case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED: + errorMessage += " " + getString(R.string.start_error_already_started); + break; + case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE: + errorMessage += " " + getString(R.string.start_error_too_large); + break; + case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED: + errorMessage += " " + getString(R.string.start_error_unsupported); + break; + case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR: + errorMessage += " " + getString(R.string.start_error_internal); + break; + case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS: + errorMessage += " " + getString(R.string.start_error_too_many); + break; + } + + Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show(); + + } + + @Override + public void onStartSuccess(AdvertiseSettings settingsInEffect) { + super.onStartSuccess(settingsInEffect); + // Don't need to do anything here, advertising successfully started. + } + } + +} diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/Constants.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/Constants.java new file mode 100644 index 000000000..d3941e2ab --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/Constants.java @@ -0,0 +1,22 @@ +package com.example.android.bluetoothadvertisements; + +import android.os.ParcelUuid; + +/** + * Constants for use in the Bluetooth Advertisements sample + */ +public class Constants { + + /** + * UUID identified with this app - set as Service UUID for BLE Advertisements. + * + * Bluetooth requires a certain format for UUIDs associated with Services. + * The official specification can be found here: + * {@link https://www.bluetooth.org/en-us/specification/assigned-numbers/service-discovery} + */ + public static final ParcelUuid Service_UUID = ParcelUuid + .fromString("0000b81d-0000-1000-8000-00805f9b34fb"); + + public static final int REQUEST_ENABLE_BT = 1; + +} diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java new file mode 100644 index 000000000..f0044a3e8 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java @@ -0,0 +1,130 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.bluetoothadvertisements; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.widget.TextView; +import android.widget.Toast; + +/** + * Setup display fragments and ensure the device supports Bluetooth. + */ +public class MainActivity extends FragmentActivity { + + private BluetoothAdapter mBluetoothAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + setTitle(R.string.activity_main_title); + + if (savedInstanceState == null ) { + + mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)) + .getAdapter(); + + // Is Bluetooth supported on this device? + if (mBluetoothAdapter != null) { + + // Is Bluetooth turned on? + if (mBluetoothAdapter.isEnabled()) { + + // Are Bluetooth Advertisements supported on this device? + if (mBluetoothAdapter.isMultipleAdvertisementSupported()) { + + // Everything is supported and enabled, load the fragments. + setupFragments(); + + } else { + + // Bluetooth Advertisements are not supported. + showErrorText(R.string.bt_ads_not_supported); + } + } else { + + // Prompt user to turn on Bluetooth (logic continues in onActivityResult()). + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT); + } + } else { + + // Bluetooth is not supported. + showErrorText(R.string.bt_not_supported); + } + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case Constants.REQUEST_ENABLE_BT: + + if (resultCode == RESULT_OK) { + + // Bluetooth is now Enabled, are Bluetooth Advertisements supported on + // this device? + if (mBluetoothAdapter.isMultipleAdvertisementSupported()) { + + // Everything is supported and enabled, load the fragments. + setupFragments(); + + } else { + + // Bluetooth Advertisements are not supported. + showErrorText(R.string.bt_ads_not_supported); + } + } else { + + // User declined to enable Bluetooth, exit the app. + Toast.makeText(this, R.string.bt_not_enabled_leaving, + Toast.LENGTH_SHORT).show(); + finish(); + } + + default: + super.onActivityResult(requestCode, resultCode, data); + } + } + + private void setupFragments() { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + + ScannerFragment scannerFragment = new ScannerFragment(); + scannerFragment.setBluetoothAdapter(mBluetoothAdapter); + transaction.replace(R.id.scanner_fragment_container, scannerFragment); + + AdvertiserFragment advertiserFragment = new AdvertiserFragment(); + advertiserFragment.setBluetoothAdapter(mBluetoothAdapter); + transaction.replace(R.id.advertiser_fragment_container, advertiserFragment); + + transaction.commit(); + } + + private void showErrorText(int messageId) { + + TextView view = (TextView) findViewById(R.id.error_textview); + view.setText(getString(messageId)); + } +} \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java new file mode 100644 index 000000000..0f905ea7a --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java @@ -0,0 +1,147 @@ +package com.example.android.bluetoothadvertisements; + +import android.bluetooth.le.ScanResult; +import android.content.Context; +import android.os.SystemClock; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +/** + * Holds and displays {@link ScanResult}s, used by {@link ScannerFragment}. + */ +public class ScanResultAdapter extends BaseAdapter { + + private ArrayList mArrayList; + + private Context mContext; + + private LayoutInflater mInflater; + + ScanResultAdapter(Context context, LayoutInflater inflater) { + super(); + mContext = context; + mInflater = inflater; + mArrayList = new ArrayList<>(); + } + + @Override + public int getCount() { + return mArrayList.size(); + } + + @Override + public Object getItem(int position) { + return mArrayList.get(position); + } + + @Override + public long getItemId(int position) { + return mArrayList.get(position).getDevice().getAddress().hashCode(); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + + // Reuse an old view if we can, otherwise create a new one. + if (view == null) { + view = mInflater.inflate(R.layout.listitem_scanresult, null); + } + + TextView deviceNameView = (TextView) view.findViewById(R.id.device_name); + TextView deviceAddressView = (TextView) view.findViewById(R.id.device_address); + TextView lastSeenView = (TextView) view.findViewById(R.id.last_seen); + + ScanResult scanResult = mArrayList.get(position); + + deviceNameView.setText(scanResult.getDevice().getName()); + deviceAddressView.setText(scanResult.getDevice().getAddress()); + lastSeenView.setText(getTimeSinceString(mContext, scanResult.getTimestampNanos())); + + return view; + } + + /** + * Search the adapter for an existing device address and return it, otherwise return -1. + */ + private int getPosition(String address) { + int position = -1; + for (int i = 0; i < mArrayList.size(); i++) { + if (mArrayList.get(i).getDevice().getAddress().equals(address)) { + position = i; + break; + } + } + return position; + } + + + /** + * Add a ScanResult item to the adapter if a result from that device isn't already present. + * Otherwise updates the existing position with the new ScanResult. + */ + public void add(ScanResult scanResult) { + + int existingPosition = getPosition(scanResult.getDevice().getAddress()); + + if (existingPosition >= 0) { + // Device is already in list, update its record. + mArrayList.set(existingPosition, scanResult); + } else { + // Add new Device's ScanResult to list. + mArrayList.add(scanResult); + } + } + + /** + * Clear out the adapter. + */ + public void clear() { + mArrayList.clear(); + } + + /** + * Takes in a number of nanoseconds and returns a human-readable string giving a vague + * description of how long ago that was. + */ + public static String getTimeSinceString(Context context, long timeNanoseconds) { + String lastSeenText = context.getResources().getString(R.string.last_seen) + " "; + + long timeSince = SystemClock.elapsedRealtimeNanos() - timeNanoseconds; + long secondsSince = TimeUnit.SECONDS.convert(timeSince, TimeUnit.NANOSECONDS); + + if (secondsSince < 5) { + lastSeenText += context.getResources().getString(R.string.just_now); + } else if (secondsSince < 60) { + lastSeenText += secondsSince + " " + context.getResources() + .getString(R.string.seconds_ago); + } else { + long minutesSince = TimeUnit.MINUTES.convert(secondsSince, TimeUnit.SECONDS); + if (minutesSince < 60) { + if (minutesSince == 1) { + lastSeenText += minutesSince + " " + context.getResources() + .getString(R.string.minute_ago); + } else { + lastSeenText += minutesSince + " " + context.getResources() + .getString(R.string.minutes_ago); + } + } else { + long hoursSince = TimeUnit.HOURS.convert(minutesSince, TimeUnit.MINUTES); + if (hoursSince == 1) { + lastSeenText += hoursSince + " " + context.getResources() + .getString(R.string.hour_ago); + } else { + lastSeenText += hoursSince + " " + context.getResources() + .getString(R.string.hours_ago); + } + } + } + + return lastSeenText; + } +} diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java new file mode 100644 index 000000000..b9ad4d966 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java @@ -0,0 +1,212 @@ +package com.example.android.bluetoothadvertisements; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.ListFragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + + +/** + * Scans for Bluetooth Low Energy Advertisements matching a filter and displays them to the user. + */ +public class ScannerFragment extends ListFragment { + + private static final String TAG = ScannerFragment.class.getSimpleName(); + + /** + * Stops scanning after 5 seconds. + */ + private static final long SCAN_PERIOD = 5000; + + private BluetoothAdapter mBluetoothAdapter; + + private BluetoothLeScanner mBluetoothLeScanner; + + private ScanCallback mScanCallback; + + private ScanResultAdapter mAdapter; + + private Handler mHandler; + + /** + * Must be called after object creation by MainActivity. + * + * @param btAdapter the local BluetoothAdapter + */ + public void setBluetoothAdapter(BluetoothAdapter btAdapter) { + this.mBluetoothAdapter = btAdapter; + mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + setRetainInstance(true); + + // Use getActivity().getApplicationContext() instead of just getActivity() because this + // object lives in a fragment and needs to be kept separate from the Activity lifecycle. + // + // We could get a LayoutInflater from the ApplicationContext but it messes with the + // default theme, so generate it from getActivity() and pass it in separately. + mAdapter = new ScanResultAdapter(getActivity().getApplicationContext(), + LayoutInflater.from(getActivity())); + mHandler = new Handler(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + final View view = super.onCreateView(inflater, container, savedInstanceState); + + setListAdapter(mAdapter); + + return view; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + getListView().setDivider(null); + getListView().setDividerHeight(0); + + setEmptyText(getString(R.string.empty_list)); + + // Trigger refresh on app's 1st load + startScanning(); + + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.scanner_menu, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.refresh: + startScanning(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * Start scanning for BLE Advertisements (& set it up to stop after a set period of time). + */ + public void startScanning() { + if (mScanCallback == null) { + Log.d(TAG, "Starting Scanning"); + + // Will stop the scanning after a set time. + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + stopScanning(); + } + }, SCAN_PERIOD); + + // Kick off a new scan. + mScanCallback = new SampleScanCallback(); + mBluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), mScanCallback); + + String toastText = getString(R.string.scan_start_toast) + " " + + TimeUnit.SECONDS.convert(SCAN_PERIOD, TimeUnit.MILLISECONDS) + " " + + getString(R.string.seconds); + Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(getActivity(), R.string.already_scanning, Toast.LENGTH_SHORT); + } + } + + /** + * Stop scanning for BLE Advertisements. + */ + public void stopScanning() { + Log.d(TAG, "Stopping Scanning"); + + // Stop the scan, wipe the callback. + mBluetoothLeScanner.stopScan(mScanCallback); + mScanCallback = null; + + // Even if no new results, update 'last seen' times. + mAdapter.notifyDataSetChanged(); + } + + /** + * Return a List of {@link ScanFilter} objects to filter by Service UUID. + */ + private List buildScanFilters() { + List scanFilters = new ArrayList<>(); + + ScanFilter.Builder builder = new ScanFilter.Builder(); + builder.setServiceUuid(Constants.Service_UUID); + scanFilters.add(builder.build()); + + return scanFilters; + } + + /** + * Return a {@link ScanSettings} object set to use low power (to preserve battery life). + */ + private ScanSettings buildScanSettings() { + ScanSettings.Builder builder = new ScanSettings.Builder(); + builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER); + return builder.build(); + } + + /** + * Custom ScanCallback object - adds to adapter on success, displays error on failure. + */ + private class SampleScanCallback extends ScanCallback { + + @Override + public void onBatchScanResults(List results) { + super.onBatchScanResults(results); + + for (ScanResult result : results) { + mAdapter.add(result); + } + mAdapter.notifyDataSetChanged(); + } + + @Override + public void onScanResult(int callbackType, ScanResult result) { + super.onScanResult(callbackType, result); + + mAdapter.add(result); + mAdapter.notifyDataSetChanged(); + } + + @Override + public void onScanFailed(int errorCode) { + super.onScanFailed(errorCode); + Toast.makeText(getActivity(), "Scan failed with error: " + errorCode, Toast.LENGTH_LONG) + .show(); + } + } +} diff --git a/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/MainActivity.java b/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/MainActivity.java index 350c9c53d..7564e6ca7 100644 --- a/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/MainActivity.java +++ b/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/MainActivity.java @@ -37,7 +37,7 @@ import android.util.Log; import android.widget.Toast; import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks; +import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; @@ -48,7 +48,7 @@ import java.util.ArrayList; import java.util.List; public class MainActivity extends Activity implements ConnectionCallbacks, - OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { + OnConnectionFailedListener { // Internal List of Geofence objects. In a real app, these might be provided by an API based on // locations within the user's proximity. @@ -143,10 +143,6 @@ public class MainActivity extends Activity implements ConnectionCallbacks, } } - @Override - public void onDisconnected() { - } - /** * Once the connection is available, send a request to add the Geofences. */ diff --git a/samples/browseable/XYZTouristAttractions/Application/AndroidManifest.xml b/samples/browseable/XYZTouristAttractions/Application/AndroidManifest.xml index 5f330d5d6..76f0198d9 100644 --- a/samples/browseable/XYZTouristAttractions/Application/AndroidManifest.xml +++ b/samples/browseable/XYZTouristAttractions/Application/AndroidManifest.xml @@ -58,6 +58,10 @@ + + diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values/colors.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/colors.xml index 142f54805..2c16a5d94 100644 --- a/samples/browseable/XYZTouristAttractions/Application/res/values/colors.xml +++ b/samples/browseable/XYZTouristAttractions/Application/res/values/colors.xml @@ -17,10 +17,6 @@ - #4e6cef - #2a36b1 - #ff7043 - #90000000 #22000000 #ddd diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/config/GlideConfiguration.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/config/GlideConfiguration.java new file mode 100644 index 000000000..77d6fc847 --- /dev/null +++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/config/GlideConfiguration.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.xyztouristattractions.config; + +import android.content.Context; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.load.DecodeFormat; +import com.bumptech.glide.module.GlideModule; + +/** + * This allows global overriding of some default Glide configuration values. + * For additional information see the Glide docs: + * https://github.com/bumptech/glide/wiki/Configuration + */ +public class GlideConfiguration implements GlideModule { + @Override + public void applyOptions(Context context, GlideBuilder builder) { + // Set Glide decode format to the higher quality ARGB_8888 format + builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); + } + + @Override + public void registerComponents(Context context, Glide glide) { + + } +} diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListActivity.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListActivity.java index 8d4311203..8d2908c2a 100644 --- a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListActivity.java +++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListActivity.java @@ -16,9 +16,9 @@ package com.example.android.xyztouristattractions.ui; -import android.app.AlertDialog; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; @@ -31,7 +31,7 @@ import com.example.android.xyztouristattractions.service.UtilityService; * The main tourist attraction activity screen which contains a list of * attractions sorted by distance. */ -public class AttractionListActivity extends ActionBarActivity { +public class AttractionListActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailActivity.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailActivity.java index a83f480c7..39507855b 100644 --- a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailActivity.java +++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailActivity.java @@ -24,7 +24,7 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityOptionsCompat; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.view.View; import com.example.android.xyztouristattractions.R; @@ -33,7 +33,7 @@ import com.example.android.xyztouristattractions.R; * The tourist attraction detail activity screen which contains the details of * a single attraction. */ -public class DetailActivity extends ActionBarActivity { +public class DetailActivity extends AppCompatActivity { private static final String EXTRA_ATTRACTION = "attraction"; diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/color/action_color.xml b/samples/browseable/XYZTouristAttractions/Shared/res/values/colors.xml similarity index 73% rename from samples/browseable/XYZTouristAttractions/Wearable/res/color/action_color.xml rename to samples/browseable/XYZTouristAttractions/Shared/res/values/colors.xml index 634d80689..73c2b6c40 100644 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/color/action_color.xml +++ b/samples/browseable/XYZTouristAttractions/Shared/res/values/colors.xml @@ -15,11 +15,10 @@ limitations under the License. --> - + - + #4e6cef + #2a36b1 + #ff7043 - - - \ No newline at end of file + \ No newline at end of file diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_open_on_device.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_open_on_device.png deleted file mode 100644 index 4e11601e81b49b55c5d1aebbf7c67295fb25e2a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3073 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 diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_openonphone.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_openonphone.png new file mode 100644 index 0000000000000000000000000000000000000000..8952486b4cadf3920d45406868bb5fd1fc225d1e GIT binary patch literal 1554 zcmZ{kdpHwn9LL|;Cd`;pA~X|D7{ex}m^HaI_ebbrLME5FY?0L2BsNY|&T)j?l}@@S zadK;9=^}DHh>pyy8>?{Ua@v{BU*~x`&-?wo-{<>#exE;nf4ncx!_5(?prHT&Ksq~7 zyrfQBFF9H1J&~7SB{f)>J=Gq7nry^c5L}ue@m`K(aH~u6tu&y6oxG?3B&;Y1&X*_}mTV*jW)wWFs=!{QPl%%q(&b3I8=( z5HX*w9-}6rySy)SnevI!rPs~%4l0m4E6Gy|WP)9!r(hd7;GmIQww4LoY!LoNbCun> zpi{f08oW2@SYCD3S@8P2wLLt2zBlV8kNAGg-E$&*KMWs+q_>l&8Zh5RqRYV}Ne zh?TSAGh>YzHz2v%tUee@Dz-#Gk~FGdeh89WX~6Sf-jTirMw<`>ANn8VZzfSX%lkF( z2Be?`uzzvYPe9+t{>)QTi7G6B1a)HYx_p;>SRyKsx7%k2UreD>*`!E)TZa$T50sjy z+VvJhm4J<;v97&*!nl7Kq5)Q@S2t$bYqLegcm)~~s(f;`40Hq23rG{b+sdlo^P$W$ zsVr}s4OQFZ`D21uoyNF!_&v4;8FyYh8TGuWS>n4Q|GVNEbgeBey{YJnnn<>X zf4gS4#}cMTY(wEpdoYY8Xs3avJT6mk^-urdB_bog&W&PwvGWjRWnnpTMRK7IIs;R& zzPL1#8Ii1q$ze8SlodRKtZE7SB;7R@eC#;(uIoFVHHiyn{+flnyMHJZaICt%9?7zt zknX}7ccx`dy%CnjI2NEvpe;MP()`sL!vhC%Vd6aTLSR`l@5j}nIVM#Gb~TPc_a9eu z)LuVoFxCN&yU)UrYgM^gxnMNMR{62$+0{6H>S9LY^VhMDR1>$0)BMcp+o&Ry z;B;(m`K-JM(j(3!C(~Ay^hxVxRfdsHi|Yg{w>X()qY;BW6E^GK;-Kl#u&Gk9s0H4lU;hbqx7)O! zY6BygZiMPHAD0!Gi;!mN(K+YshDN%s;EHwbr3|J-Lv90iXt9NPy?1XuxG>grJ486s zO4c>E#ZRV?K+TGfBjk)ezH!rWW!hf-1&1O#cTDY$WGWSC!M3rDdsV#)mPxP*{vVW zou(P_cTUKqZ^`o?ZfihA2mMeQu2BuAm%bRrBL`(PPyPFFev`*lEH@GQ0vg{{ro6xN z$oHlH@_@m~{-UbWIpSyH!Q7f&mf{(7w_TO_oAPO|w6lIskbcIKzQG}Bf`Cg@@0ORR zB3;!DsixD;Q=hK26h^hisw+-gWeL9tD}nf;a9d1RhIgg^q#o13j~Nog3?&A~g-Q*W zIh9jdF`j GOZf+c^`8^~ literal 0 HcmV?d00001 diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_open_on_device.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_open_on_device.png deleted file mode 100644 index 2f6f05639be8ce93150ac4b4cc72ddda1e812eba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1841 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 - + android:src="@drawable/ic_full_openonphone" + android:text="@string/action_open" + android:maxLines="1" + android:color="@color/colorPrimary" + app:rippleColor="@color/colorAccent" /> diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_round.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_round.xml deleted file mode 100644 index 70cec1acd..000000000 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_round.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_square.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_square.xml deleted file mode 100644 index 362671b89..000000000 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_square.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/values/styles.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/values/styles.xml index cebe1c71c..11b2d3d08 100644 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/values/styles.xml +++ b/samples/browseable/XYZTouristAttractions/Wearable/res/values/styles.xml @@ -31,18 +31,4 @@ end - - - - \ No newline at end of file diff --git a/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsActivity.java b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsActivity.java index 464eb8a42..64d28bc1e 100644 --- a/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsActivity.java +++ b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsActivity.java @@ -84,6 +84,8 @@ public class AttractionsActivity extends Activity mAdapter = new AttractionsGridPagerAdapter(this, mAttractions); mAdapter.setOnChromeFadeListener(this); mGridViewPager.setAdapter(mAdapter); + mDotsPageIndicator.setPager(mGridViewPager); + mDotsPageIndicator.setOnPageChangeListener(mAdapter); topFrameLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { @Override @@ -243,8 +245,6 @@ public class AttractionsActivity extends Activity // Update UI based on the result of the background processing mAdapter.setData(result); mAdapter.notifyDataSetChanged(); - mDotsPageIndicator.setPager(mGridViewPager); - mDotsPageIndicator.setOnPageChangeListener(mAdapter); mProgressBar.setVisibility(View.GONE); mDotsPageIndicator.setVisibility(View.VISIBLE); mGridViewPager.setVisibility(View.VISIBLE); diff --git a/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsGridPagerAdapter.java b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsGridPagerAdapter.java index 99737f4d6..97accdec9 100644 --- a/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsGridPagerAdapter.java +++ b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsGridPagerAdapter.java @@ -25,11 +25,11 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; import android.support.wearable.activity.ConfirmationActivity; +import android.support.wearable.view.ActionPage; import android.support.wearable.view.CardFrame; import android.support.wearable.view.CardScrollView; import android.support.wearable.view.GridPagerAdapter; import android.support.wearable.view.GridViewPager; -import android.support.wearable.view.WatchViewStub; import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; @@ -97,7 +97,7 @@ public class AttractionsGridPagerAdapter extends GridPagerAdapter } @Override - protected Object instantiateItem(ViewGroup container, int row, final int column) { + public Object instantiateItem(ViewGroup container, int row, final int column) { if (mAttractions != null && mAttractions.size() > 0) { final Attraction attraction = mAttractions.get(row); switch (column) { @@ -150,48 +150,30 @@ public class AttractionsGridPagerAdapter extends GridPagerAdapter return cardScrollView; case PAGER_NAVIGATE_ACTION_COLUMN: // The navigate action - final WatchViewStub navStub = (WatchViewStub) mLayoutInflater.inflate( + final ActionPage navActionPage = (ActionPage) mLayoutInflater.inflate( R.layout.gridpager_action, container, false); - navStub.setOnClickListener(getStartActionClickListener( + navActionPage.setOnClickListener(getStartActionClickListener( attraction, Constants.START_NAVIGATION_PATH, ConfirmationActivity.SUCCESS_ANIMATION)); + navActionPage.setImageResource(R.drawable.ic_full_directions_walking); + navActionPage.setText(mContext.getString(R.string.action_navigate)); - navStub.setOnLayoutInflatedListener( - new WatchViewStub.OnLayoutInflatedListener() { - @Override - public void onLayoutInflated(WatchViewStub watchViewStub) { - ImageView imageView = (ImageView) navStub.findViewById(R.id.imageView); - imageView.setImageResource(R.drawable.ic_full_directions_walking); - TextView textView = (TextView) navStub.findViewById(R.id.textView); - textView.setText(R.string.action_navigate); - } - }); - - container.addView(navStub); - return navStub; + container.addView(navActionPage); + return navActionPage; case PAGER_OPEN_ACTION_COLUMN: // The "open on device" action - final WatchViewStub openStub = (WatchViewStub) mLayoutInflater.inflate( + final ActionPage openActionPage = (ActionPage) mLayoutInflater.inflate( R.layout.gridpager_action, container, false); - openStub.setOnClickListener(getStartActionClickListener( + openActionPage.setOnClickListener(getStartActionClickListener( attraction, Constants.START_ATTRACTION_PATH, ConfirmationActivity.OPEN_ON_PHONE_ANIMATION)); + openActionPage.setImageResource(R.drawable.ic_full_openonphone); + openActionPage.setText(mContext.getString(R.string.action_open)); - openStub.setOnLayoutInflatedListener( - new WatchViewStub.OnLayoutInflatedListener() { - @Override - public void onLayoutInflated(WatchViewStub watchViewStub) { - ImageView imageView = (ImageView) openStub.findViewById(R.id.imageView); - imageView.setImageResource(R.drawable.ic_full_open_on_device); - TextView textView = (TextView) openStub.findViewById(R.id.textView); - textView.setText(R.string.action_open); - } - }); - - container.addView(openStub); - return openStub; + container.addView(openActionPage); + return openActionPage; } } return new View(mContext); @@ -209,7 +191,7 @@ public class AttractionsGridPagerAdapter extends GridPagerAdapter } @Override - protected void destroyItem(ViewGroup viewGroup, int row, int column, Object object) { + public void destroyItem(ViewGroup viewGroup, int row, int column, Object object) { mDelayedHide.remove((View) object); viewGroup.removeView((View)object); } @@ -242,6 +224,7 @@ public class AttractionsGridPagerAdapter extends GridPagerAdapter private void startAction(Attraction attraction, String pathName, int confirmAnimationType) { Intent intent = new Intent(mContext, ConfirmationActivity.class); intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, confirmAnimationType); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); mContext.startActivity(intent); UtilityService.clearNotification(mContext); @@ -270,8 +253,8 @@ public class AttractionsGridPagerAdapter extends GridPagerAdapter } public interface OnChromeFadeListener { - abstract void onChromeFadeIn(); - abstract void onChromeFadeOut(); + void onChromeFadeIn(); + void onChromeFadeOut(); } /**