diff options
author | Trevor Johns <trevorjohns@google.com> | 2015-05-08 19:29:42 -0700 |
---|---|---|
committer | Trevor Johns <trevorjohns@google.com> | 2015-05-08 19:29:42 -0700 |
commit | 434d41c45df023fd543a94e88b0bda2dc5c7844d (patch) | |
tree | 11589625b30085c4803f5edb544de3305c2fa3fe | |
parent | 03236391f11efe2a9ce89ab92602c58ba717f626 (diff) | |
download | android_development-434d41c45df023fd543a94e88b0bda2dc5c7844d.tar.gz android_development-434d41c45df023fd543a94e88b0bda2dc5c7844d.tar.bz2 android_development-434d41c45df023fd543a94e88b0bda2dc5c7844d.zip |
Update samples prebuilts for lmp-mr1-ub-docs
Synced to developers/samples/android commit
54bab34b386e343e9d0ea75a5fb8d13db2c71eb5.
Change-Id: I1c9d9d2c1f53a051d7b4d85303d5c01ab6f16e68
55 files changed, 1823 insertions, 201 deletions
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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.wearable.wear.alwayson"> + + <uses-sdk android:minSdkVersion="20" android:targetSdkVersion="22" /> + + <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/> + + <uses-feature android:name="android.hardware.type.watch" /> + + <application + android:allowBackup="false" + android:label="@string/app_name"> + + <!--If you want your app to run on pre-22, then set required to false --> + <uses-library android:name="com.google.android.wearable" android:required="false" /> + + <activity android:name="com.example.android.wearable.wear.alwayson.MainActivity" + android:label="@string/app_name" + android:launchMode="singleInstance" + android:configChanges="orientation|keyboardHidden" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + </application> +</manifest> 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 + +<p> + + Demonstrates a native Android Wear app using ambient screen support. + > + </p> 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 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright (C) 2015 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. +--> +<android.support.wearable.view.WatchViewStub + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/watch_view_stub" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:rectLayout="@layout/rect_activity_main" + app:roundLayout="@layout/round_activity_main" + tools:context=".MainActivity" + tools:deviceIds="wear"> +</android.support.wearable.view.WatchViewStub> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:paddingTop="@dimen/square_top_margin" + android:paddingLeft="@dimen/square_left_margin" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + tools:context=".MainActivity" + tools:deviceIds="wear_square"> + + <TextView + android:id="@+id/time" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="24sp" + android:text="Hello, time!"/> + + <TextView + android:id="@+id/time_stamp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, timestamp!"/> + + <TextView + android:id="@+id/state" + android:textColor="@color/green" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, state!"/> + + <TextView + android:id="@+id/update_rate" + android:textColor="@color/green" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, update rate!"/> + + <TextView + android:id="@+id/draw_count" + android:textColor="@color/green" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, draw count!"/> +</LinearLayout> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:paddingTop="@dimen/round_top_margin" + android:paddingLeft="@dimen/round_left_margin" + android:layout_width="match_parent" + android:orientation="vertical" + android:layout_height="match_parent" + tools:context=".MainActivity" + tools:deviceIds="wear_round"> + + <TextView + android:id="@+id/time" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="24sp" + android:text="Hello, time!"/> + + <TextView + android:id="@+id/time_stamp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, timestamp!"/> + + <TextView + android:id="@+id/state" + android:textColor="@color/green" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, state!"/> + + <TextView + android:id="@+id/update_rate" + android:textColor="@color/green" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, update rate!"/> + + <TextView + android:id="@+id/draw_count" + android:textColor="@color/green" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Hello, draw count!"/> +</LinearLayout> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="square_top_margin">24dp</dimen> + <dimen name="square_left_margin">16dp</dimen> + + <dimen name="round_top_margin">34dp</dimen> + <dimen name="round_left_margin">34dp</dimen> +</resources> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_name">Always On Example</string> + <string name="timestamp_label">Timestamp: %1$d</string> + <string name="mode_active_label">Active Mode (Handler)</string> + <string name="mode_ambient_label">Ambient Mode (Alarm)</string> + <string name="update_rate_label">Update rate: %1$d sec</string> + <string name="draw_count_label">Draw count: %1$d</string> +</resources> 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<MainActivity> mMainActivityWeakReference; + + public UpdateHandler(MainActivity reference) { + mMainActivityWeakReference = new WeakReference<MainActivity>(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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.bluetoothadvertisements" + android:versionCode="1" + android:versionName="1.0"> + + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> + <uses-permission android:name="android.permission.BLUETOOTH"/> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> 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 + +<p> + +This samples demonstrates how to use the Bluetooth Low Power Advertisements API +with a single device acting as both scanner and advertiser. + + </p> diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_action_refresh.png b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..dae27903e --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_action_refresh.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b1efaf4b2 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/tile.9.png b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_action_refresh.png b/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..94ab6f4c5 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_action_refresh.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f5f9244f2 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_action_refresh.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..ab4ab9da6 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_action_refresh.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..5d07b3f06 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_action_refresh.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_action_refresh.png Binary files differnew file mode 100644 index 000000000..44ee117ee --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_action_refresh.png diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6ef21e1f4 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:id="@+id/sample_main_layout"> + + <FrameLayout + android:id="@+id/scanner_fragment_container" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="0px" > + + <TextView + android:id="@+id/error_textview" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:freezesText="true" /> + + </FrameLayout> + + <FrameLayout + android:id="@+id/advertiser_fragment_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <!--<fragment--> + <!--android:name="com.example.android.bluetoothadvertisements.ScannerFragment"--> + <!--android:id="@+id/scanner_fragment"--> + <!--android:layout_width="match_parent"--> + <!--android:layout_height="wrap_content" />--> + + <!--<fragment--> + <!--android:name="com.example.android.bluetoothadvertisements.AdvertiserFragment"--> + <!--android:id="@+id/advertiser_fragment"--> + <!--android:layout_width="match_parent"--> + <!--android:layout_height="wrap_content" />--> + +</LinearLayout> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:context="com.example.android.bluetoothadvertisements.AdvertiserFragment"> + + <!-- Horizontal Divider --> + <View + android:layout_width="250dp" + android:layout_height="1dp" + android:layout_centerHorizontal="true" + android:layout_alignParentTop="true" + android:background="@android:color/darker_gray"/> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_centerInParent="true" + android:paddingTop="20dp" + android:paddingBottom="20dp" > + + <TextView + android:text="@string/broadcast_device" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="100dp" + android:padding="5dp"/> + + <Switch + android:id="@+id/advertise_switch" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:switchMinWidth="80dp" /> + + </LinearLayout> + +</RelativeLayout> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + +<LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="20dp" + android:paddingLeft="100dp" + android:paddingRight="100dp"> + <TextView android:id="@+id/device_name" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="16dp"/> + + <TextView android:id="@+id/device_address" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="12dp"/> + <TextView android:id="@+id/last_seen" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="12dp"/> +</LinearLayout> +</RelativeLayout>
\ 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/refresh" + android:title="@string/refresh" + android:showAsAction="always" + android:icon="@drawable/ic_action_refresh" + /> +</menu>
\ 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 @@ +<!-- + 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. + --> + +<resources> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> 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 @@ +<!-- + 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. + --> + +<resources> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> 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 @@ +<!-- + 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. + --> + +<resources> + + <!-- Activity themes --> + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + +</resources> diff --git a/samples/browseable/BluetoothAdvertisements/res/values-v21/base-colors.xml b/samples/browseable/BluetoothAdvertisements/res/values-v21/base-colors.xml new file mode 100644 index 000000000..d90237837 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values-v21/base-colors.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<resources> + + <color name="colorPrimary">#434AB3</color> + <color name="colorPrimaryDark">#34379D</color> + <color name="textColorPrimary">#FFFFFF</color> + +</resources> diff --git a/samples/browseable/BluetoothAdvertisements/res/values-v21/base-template-styles.xml b/samples/browseable/BluetoothAdvertisements/res/values-v21/base-template-styles.xml new file mode 100644 index 000000000..023485609 --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/res/values-v21/base-template-styles.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<resources> + + <!-- Activity themes --> + <style name="Theme.Base" parent="android:Theme.Material.Light"> + <item name="android:colorPrimary">@color/colorPrimary</item> + <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="android:textColorPrimary">@color/textColorPrimary</item> + </style> + +</resources> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<resources> + <string name="app_name">BluetoothAdvertisements</string> + <string name="intro_message"> + <![CDATA[ + + +This samples demonstrates how to use the Bluetooth Low Power Advertisements API +with a single device acting as both scanner and advertiser. + + + ]]> + </string> +</resources> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="bt_not_enabled_leaving">User declined to enable Bluetooth, exiting Bluetooth Advertisements.</string> + <string name="activity_main_title">Nearby Devices</string> + <string name="broadcast_device">Broadcast Device</string> + + <string name="bt_not_supported">Bluetooth is not supported on this device.</string> + <string name="bt_ads_not_supported">Bluetooth Advertisements are not supported on this device.</string> + + <string name="refresh">Refresh</string> + <string name="start_error_prefix">Start Advertising failed: </string> + <string name="start_error_already_started">already started.</string> + <string name="start_error_too_large">data packet exceeded 31 Byte limit.</string> + <string name="start_error_unsupported">not supported on this device.</string> + <string name="start_error_internal">internal error.</string> + <string name="start_error_too_many">too many advertisers.</string> + <string name="bt_null">Error: Bluetooth object null</string> + <string name="last_seen">Last Seen:</string> + <string name="just_now">just now</string> + <string name="minute_ago">minute ago</string> + <string name="hour_ago">hour ago</string> + <string name="seconds_ago">seconds ago</string> + <string name="minutes_ago">minutes ago</string> + <string name="hours_ago">hours ago</string> + <string name="empty_list">No devices found - refresh to try again.</string> + <string name="seconds">seconds.</string> + <string name="scan_start_toast">Scanning for</string> + <string name="already_scanning">Scanning already started.</string> + +</resources>
\ 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 @@ +<!-- + 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. + --> + +<resources> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> 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 @@ +<!-- + 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. + --> + +<resources> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> 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<ScanResult> 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<ScanFilter> buildScanFilters() { + List<ScanFilter> 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<ScanResult> 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 @@ <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> + <meta-data + android:name="com.example.android.xyztouristattractions.config.GlideConfiguration" + android:value="GlideModule"/> + </application> </manifest> 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 @@ <resources> - <color name="colorPrimary">#4e6cef</color> - <color name="colorPrimaryDark">#2a36b1</color> - <color name="colorAccent">#ff7043</color> - <color name="text_background">#90000000</color> <color name="transparent_actionbar_background">#22000000</color> <color name="lighter_gray">#ddd</color> 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 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. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<resources> - <item android:state_pressed="true" - android:color="#ee3c4b90" /> <!-- pressed --> + <color name="colorPrimary">#4e6cef</color> + <color name="colorPrimaryDark">#2a36b1</color> + <color name="colorAccent">#ff7043</color> - <item android:color="#ee5c6bc0" /> <!-- default --> - -</selector>
\ No newline at end of file +</resources>
\ 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 Binary files differdeleted file mode 100644 index 4e11601e8..000000000 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_open_on_device.png +++ /dev/null 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 Binary files differnew file mode 100644 index 000000000..8952486b4 --- /dev/null +++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_openonphone.png 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 Binary files differdeleted file mode 100644 index 2f6f05639..000000000 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_open_on_device.png +++ /dev/null diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_openonphone.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_openonphone.png Binary files differnew file mode 100644 index 000000000..d10a19c95 --- /dev/null +++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_openonphone.png diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/activity_main.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/activity_main.xml index a4ef94be2..27fce1f18 100644 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/activity_main.xml +++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/activity_main.xml @@ -41,9 +41,6 @@ android:layout_height="wrap_content" android:layout_width="wrap_content" app:dotFadeWhenIdle="false" - app:dotFadeInDuration="0" - app:dotFadeOutDuration="0" - app:dotFadeOutDelay="0" android:visibility="gone" /> <android.support.wearable.view.DismissOverlayView diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action.xml index 4b3bbaf5f..ac01509fd 100644 --- a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action.xml +++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action.xml @@ -16,11 +16,14 @@ limitations under the License. --> -<android.support.wearable.view.WatchViewStub +<android.support.wearable.view.ActionPage xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/actionpage" android:layout_width="match_parent" android:layout_height="match_parent" - app:rectLayout="@layout/gridpager_action_square" - app:roundLayout="@layout/gridpager_action_round" - android:clickable="true" /> + 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 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - 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. - --> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <View - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="#7F000000"/> - - <android.support.wearable.view.CircledImageView - android:id="@+id/circleImageView" - android:layout_width="112dp" - android:layout_height="112dp" - android:layout_centerInParent="true" - app:circle_radius="52dp" - app:circle_radius_pressed="56dp" - app:circle_color="@color/action_color"> - - <ImageView - android:id="@+id/imageView" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_gravity="center" - android:src="@drawable/ic_full_open_on_device" - android:scaleType="centerCrop" /> - - </android.support.wearable.view.CircledImageView> - - <TextView - android:id="@+id/textView" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@style/ActionTextStyleRound" - android:layout_below="@id/circleImageView" - android:layout_centerHorizontal="true" - android:gravity="center" - tools:text="Navigate" /> - -</RelativeLayout>
\ 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 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- - 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. - --> - -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <View - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="#7F000000"/> - - <android.support.wearable.view.CircledImageView - android:id="@+id/circleImageView" - android:layout_width="112dp" - android:layout_height="112dp" - android:layout_alignParentTop="true" - android:layout_centerHorizontal="true" - android:layout_marginTop="24dp" - app:circle_radius="52dp" - app:circle_radius_pressed="56dp" - app:circle_color="@color/action_color"> - - <ImageView - android:id="@+id/imageView" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_gravity="center" - android:src="@drawable/ic_full_open_on_device" - android:scaleType="centerCrop" /> - - </android.support.wearable.view.CircledImageView> - - <TextView - android:id="@+id/textView" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@style/ActionTextStyle" - android:layout_below="@id/circleImageView" - android:layout_marginBottom="12dp" - android:layout_centerHorizontal="true" - android:maxLines="2" - android:gravity="center" - tools:text="Navigate" /> - -</RelativeLayout>
\ 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 @@ <item name="android:ellipsize">end</item> </style> - <style name="ActionTextStyle" parent="@android:style/TextAppearance.Large"> - <item name="android:fontFamily">sans-serif-condensed-light</item> - <item name="android:textStyle">normal</item> - <item name="android:textSize">18sp</item> - <item name="android:maxLines">2</item> - <item name="android:ellipsize">end</item> - <item name="android:textColor">#FFFFFF</item> - </style> - - <style name="ActionTextStyleRound" parent="ActionTextStyle"> - <item name="android:textSize">14sp</item> - <item name="android:maxLines">1</item> - </style> - </resources>
\ 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(); } /** |