am 0d473ea1: am 721cd1b8: am 77b5d394: Update sdk.atree and samples/browseable for lastest samples release
* commit '0d473ea178b5120ae45249c4d644cd00e876e63e':
Update sdk.atree and samples/browseable for lastest samples release
diff --git a/build/sdk.atree b/build/sdk.atree
index 0628173..269f4f9 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -280,9 +280,7 @@
developers/build/prebuilts/gradle/BatchStepSensor samples/${PLATFORM_NAME}/sensors/BatchStepSensor
developers/build/prebuilts/gradle/DisplayingBitmaps samples/${PLATFORM_NAME}/ui/DisplayingBitmaps
developers/build/prebuilts/gradle/BasicTransition samples/${PLATFORM_NAME}/ui/BasicTransition
-developers/build/prebuilts/gradle/AdapterTransition samples/${PLATFORM_NAME}/ui/AdapterTransition
developers/build/prebuilts/gradle/CustomTransition samples/${PLATFORM_NAME}/ui/CustomTransition
-developers/build/prebuilts/gradle/FragmentTransition samples/${PLATFORM_NAME}/ui/FragmentTransition
developers/build/prebuilts/gradle/SwipeRefreshLayoutBasic samples/${PLATFORM_NAME}/ui/SwipeRefreshLayoutBasic
developers/build/prebuilts/gradle/SwipeRefreshListFragment samples/${PLATFORM_NAME}/ui/SwipeRefreshListFragment
developers/build/prebuilts/gradle/SwipeRefreshMultipleViews samples/${PLATFORM_NAME}/ui/SwipeRefreshMultipleViews
@@ -314,6 +312,8 @@
developers/build/prebuilts/gradle/DirectorySelection samples/${PLATFORM_NAME}/content/documentsUi/DirectorySelection
developers/build/prebuilts/gradle/AppUsageStatistics samples/${PLATFORM_NAME}/system/AppUsageStatistics
developers/build/prebuilts/gradle/ScreenCapture samples/${PLATFORM_NAME}/media/ScreenCapture
+developers/build/prebuilts/gradle/NfcProvisioning samples/${PLATFORM_NAME}/nfc/NfcProvisioning
+developers/build/prebuilts/gradle/DeviceOwner samples/${PLATFORM_NAME}/admin/DeviceOwner
developers/build/prebuilts/androidtv samples/${PLATFORM_NAME}/androidtv
@@ -322,7 +322,6 @@
developers/build/prebuilts/gradle/DataLayer samples/${PLATFORM_NAME}/wearable/DataLayer
developers/build/prebuilts/gradle/DelayedConfirmation samples/${PLATFORM_NAME}/wearable/DelayedConfirmation
developers/build/prebuilts/gradle/ElizaChat samples/${PLATFORM_NAME}/wearable/ElizaChat
-developers/build/prebuilts/gradle/EmbeddedApp samples/${PLATFORM_NAME}/wearable/EmbeddedApp
developers/build/prebuilts/gradle/FindMyPhone samples/${PLATFORM_NAME}/wearable/FindMyPhone
developers/build/prebuilts/gradle/Flashlight samples/${PLATFORM_NAME}/wearable/Flashlight
developers/build/prebuilts/gradle/Geofencing samples/${PLATFORM_NAME}/wearable/Geofencing
@@ -337,6 +336,7 @@
developers/build/prebuilts/gradle/Timer samples/${PLATFORM_NAME}/wearable/Timer
developers/build/prebuilts/gradle/WatchFace samples/${PLATFORM_NAME}/wearable/WatchFace
developers/build/prebuilts/gradle/WatchViewStub samples/${PLATFORM_NAME}/wearable/WatchViewStub
+developers/build/prebuilts/gradle/XYZTouristAttractions samples/${PLATFORM_NAME}/wearable/XYZTouristAttractions
# Old sample tree
development/samples/AccelerometerPlay samples/${PLATFORM_NAME}/legacy/AccelerometerPlay
@@ -503,4 +503,3 @@
##############################################################################
framework/layoutlib-tests.jar tests/libtests/layoutlib-tests.jar
system/app/EmulatorSmokeTests/EmulatorSmokeTests.apk tests/emulator-test-apps/EmulatorSmokeTests.apk
-
diff --git a/samples/browseable/ActionBarCompat-Basic/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-Basic/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/ActionBarCompat-Basic/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/ActionBarCompat-ListPopupMenu/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/ActionBarCompat-Styled/res/layout/activity_main.xml b/samples/browseable/ActionBarCompat-Styled/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/ActionBarCompat-Styled/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/ActivityInstrumentation/res/layout/activity_main.xml b/samples/browseable/ActivityInstrumentation/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/ActivityInstrumentation/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/ActivitySceneTransitionBasic/res/layout/activity_main.xml b/samples/browseable/ActivitySceneTransitionBasic/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/ActivitySceneTransitionBasic/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/AdapterTransition/AndroidManifest.xml b/samples/browseable/AdapterTransition/AndroidManifest.xml
deleted file mode 100644
index a998a4c..0000000
--- a/samples/browseable/AdapterTransition/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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.adaptertransition"
- android:versionCode="1"
- android:versionName="1.0">
-
- <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
-
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme">
- <activity
- android:name="com.example.android.adaptertransition.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/AdapterTransition/_index.jd b/samples/browseable/AdapterTransition/_index.jd
deleted file mode 100644
index a2d134a..0000000
--- a/samples/browseable/AdapterTransition/_index.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.tags="AdapterTransition"
-sample.group=UI
-@jd:body
-
-<p>
-
- Transition cannot be directly applied to AdapterViews. In this sample, we demonstrate how to create an overlay layout and run a Transition on it. Press the action bar button to toggle between ListView and GridView.
-
- </p>
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_grid.png
deleted file mode 100644
index e04f4a7..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_grid.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_list.png
deleted file mode 100644
index 4131dba..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_action_list.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index b7a67c0..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_grid.png
deleted file mode 100644
index f2a83e3..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_grid.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_list.png
deleted file mode 100644
index e248a48..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_action_list.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 1c9fc09..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p1.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p1.jpg
deleted file mode 100644
index 10f07ac..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p1.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p10.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p10.jpg
deleted file mode 100644
index 4272f4c..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p10.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p11.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p11.jpg
deleted file mode 100644
index c5722b2..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p11.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p2.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p2.jpg
deleted file mode 100644
index ca380ae..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p2.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p3.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p3.jpg
deleted file mode 100644
index 6fc71e7..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p3.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p4.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p4.jpg
deleted file mode 100644
index 153c1ff..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p4.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p5.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p5.jpg
deleted file mode 100644
index 46d6a13..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p5.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p6.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p6.jpg
deleted file mode 100644
index 89ccb83..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p6.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p7.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p7.jpg
deleted file mode 100644
index 7e9546d..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p7.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p8.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p8.jpg
deleted file mode 100644
index 21e25ba..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p8.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-nodpi/p9.jpg b/samples/browseable/AdapterTransition/res/drawable-nodpi/p9.jpg
deleted file mode 100644
index 79854cb..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-nodpi/p9.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_grid.png
deleted file mode 100644
index ecd39b5..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_grid.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_list.png
deleted file mode 100644
index e7e510d..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_action_list.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 11b9928..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_grid.png b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_grid.png
deleted file mode 100644
index 3ba98fc..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_grid.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_list.png b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_list.png
deleted file mode 100644
index d187732..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_action_list.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index f136c9f..0000000
--- a/samples/browseable/AdapterTransition/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/layout/fragment_adapter_transition.xml b/samples/browseable/AdapterTransition/res/layout/fragment_adapter_transition.xml
deleted file mode 100644
index 22ec090..0000000
--- a/samples/browseable/AdapterTransition/res/layout/fragment_adapter_transition.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context="com.example.android.adaptertransition.AdapterTransitionFragment">
-
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- <FrameLayout
- android:id="@+id/cover"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#f3f3f3"
- android:visibility="invisible"/>
-
-</FrameLayout>
diff --git a/samples/browseable/AdapterTransition/res/layout/fragment_meat_grid.xml b/samples/browseable/AdapterTransition/res/layout/fragment_meat_grid.xml
deleted file mode 100644
index 9a4f7a1..0000000
--- a/samples/browseable/AdapterTransition/res/layout/fragment_meat_grid.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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.
--->
-<GridView
- android:id="@+id/abs_list_view"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:columnWidth="150dp"
- android:horizontalSpacing="1dp"
- android:numColumns="auto_fit"
- android:padding="1dp"
- android:scrollbars="none"
- android:stretchMode="columnWidth"
- android:verticalSpacing="1dp"/>
diff --git a/samples/browseable/AdapterTransition/res/layout/fragment_meat_list.xml b/samples/browseable/AdapterTransition/res/layout/fragment_meat_list.xml
deleted file mode 100644
index 4523b26..0000000
--- a/samples/browseable/AdapterTransition/res/layout/fragment_meat_list.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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.
--->
-<ListView
- android:id="@+id/abs_list_view"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
diff --git a/samples/browseable/AdapterTransition/res/layout/item_meat_grid.xml b/samples/browseable/AdapterTransition/res/layout/item_meat_grid.xml
deleted file mode 100644
index d7fb77a..0000000
--- a/samples/browseable/AdapterTransition/res/layout/item_meat_grid.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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
- android:id="@+id/meat_container"
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="150dp">
-
- <ImageView
- android:id="@+id/meat_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- tools:src="@drawable/p1"/>
-
- <TextView
- android:id="@+id/meat_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="16dp"
- android:layout_marginStart="16dp"
- android:gravity="center_horizontal"
- android:shadowColor="#000000"
- android:shadowDx="0"
- android:shadowDy="0"
- android:shadowRadius="10"
- android:textColor="#ffffff"
- android:textSize="24sp"
- android:textStyle="bold"
- tools:text="Hello"/>
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/AdapterTransition/res/layout/item_meat_list.xml b/samples/browseable/AdapterTransition/res/layout/item_meat_list.xml
deleted file mode 100644
index 8d75b90..0000000
--- a/samples/browseable/AdapterTransition/res/layout/item_meat_list.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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
- android:id="@+id/meat_container"
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart">
-
- <ImageView
- android:id="@+id/meat_image"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:scaleType="centerCrop"
- tools:src="@drawable/p1"/>
-
- <TextView
- android:id="@+id/meat_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
- android:layout_toEndOf="@id/meat_image"
- android:layout_centerInParent="true"
- android:gravity="center_vertical"
- android:textSize="24sp"
- tools:text="Title"/>
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/AdapterTransition/res/menu/fragment_adapter_transition.xml b/samples/browseable/AdapterTransition/res/menu/fragment_adapter_transition.xml
deleted file mode 100644
index 10057b8..0000000
--- a/samples/browseable/AdapterTransition/res/menu/fragment_adapter_transition.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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.
--->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/action_toggle"
- android:icon="@drawable/ic_action_grid"
- android:showAsAction="always|withText"
- android:title="@string/show_as_grid"/>
-</menu>
diff --git a/samples/browseable/AdapterTransition/res/values/base-strings.xml b/samples/browseable/AdapterTransition/res/values/base-strings.xml
deleted file mode 100644
index 09f86cd..0000000
--- a/samples/browseable/AdapterTransition/res/values/base-strings.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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">AdapterTransition</string>
- <string name="intro_message">
- <![CDATA[
-
-
- Transition cannot be directly applied to AdapterViews. In this sample, we demonstrate how to create an overlay layout and run a Transition on it. Press the action bar button to toggle between ListView and GridView.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/AdapterTransitionFragment.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/AdapterTransitionFragment.java
deleted file mode 100644
index a949818..0000000
--- a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/AdapterTransitionFragment.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright 2014 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.adaptertransition;
-
-import android.os.Bundle;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.Fragment;
-import android.transition.AutoTransition;
-import android.transition.Scene;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-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.AbsListView;
-import android.widget.FrameLayout;
-import android.widget.GridView;
-import android.widget.ListView;
-import android.widget.Toast;
-
-/**
- * Main screen for AdapterTransition sample.
- */
-public class AdapterTransitionFragment extends Fragment implements Transition.TransitionListener {
-
- /**
- * Since the transition framework requires all relevant views in a view hierarchy to be marked
- * with IDs, we use this ID to mark the root view.
- */
- private static final int ROOT_ID = 1;
-
- /**
- * A tag for saving state whether the mAbsListView is ListView or GridView.
- */
- private static final String STATE_IS_LISTVIEW = "is_listview";
-
- /**
- * This is where we place our AdapterView (ListView / GridView).
- */
- private FrameLayout mContent;
-
- /**
- * This is where we carry out the transition.
- */
- private FrameLayout mCover;
-
- /**
- * This list shows our contents. It can be ListView or GridView, and we toggle between them
- * using the transition framework.
- */
- private AbsListView mAbsListView;
-
- /**
- * This is our contents.
- */
- private MeatAdapter mAdapter;
-
- public static AdapterTransitionFragment newInstance() {
- return new AdapterTransitionFragment();
- }
-
- public AdapterTransitionFragment() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // If savedInstanceState is available, we restore the state whether the list is a ListView
- // or a GridView.
- boolean isListView;
- if (null == savedInstanceState) {
- isListView = true;
- } else {
- isListView = savedInstanceState.getBoolean(STATE_IS_LISTVIEW, true);
- }
- inflateAbsList(inflater, container, isListView);
- return inflater.inflate(R.layout.fragment_adapter_transition, container, false);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(STATE_IS_LISTVIEW, mAbsListView instanceof ListView);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- // Retaining references for FrameLayouts that we use later.
- mContent = (FrameLayout) view.findViewById(R.id.content);
- mCover = (FrameLayout) view.findViewById(R.id.cover);
- // We are attaching the list to the screen here.
- mContent.addView(mAbsListView);
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.fragment_adapter_transition, menu);
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- // We change the look of the icon every time the user toggles between list and grid.
- MenuItem item = menu.findItem(R.id.action_toggle);
- if (null != item) {
- if (mAbsListView instanceof ListView) {
- item.setIcon(R.drawable.ic_action_grid);
- item.setTitle(R.string.show_as_grid);
- } else {
- item.setIcon(R.drawable.ic_action_list);
- item.setTitle(R.string.show_as_list);
- }
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_toggle: {
- toggle();
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onTransitionStart(Transition transition) {
- }
-
- // BEGIN_INCLUDE(on_transition_end)
- @Override
- public void onTransitionEnd(Transition transition) {
- // When the transition ends, we remove all the views from the overlay and hide it.
- mCover.removeAllViews();
- mCover.setVisibility(View.INVISIBLE);
- }
- // END_INCLUDE(on_transition_end)
-
- @Override
- public void onTransitionCancel(Transition transition) {
- }
-
- @Override
- public void onTransitionPause(Transition transition) {
- }
-
- @Override
- public void onTransitionResume(Transition transition) {
- }
-
- /**
- * Inflate a ListView or a GridView with a corresponding ListAdapter.
- *
- * @param inflater The LayoutInflater.
- * @param container The ViewGroup that contains this AbsListView. The AbsListView won't be
- * attached to it.
- * @param inflateListView Pass true to inflate a ListView, or false to inflate a GridView.
- */
- private void inflateAbsList(LayoutInflater inflater, ViewGroup container,
- boolean inflateListView) {
- if (inflateListView) {
- mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_list,
- container, false);
- mAdapter = new MeatAdapter(inflater, R.layout.item_meat_list);
- } else {
- mAbsListView = (AbsListView) inflater.inflate(R.layout.fragment_meat_grid,
- container, false);
- mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid);
- }
- mAbsListView.setAdapter(mAdapter);
- mAbsListView.setOnItemClickListener(mAdapter);
- }
-
- /**
- * Toggle the UI between ListView and GridView.
- */
- private void toggle() {
- // We use mCover as the overlay on which we carry out the transition.
- mCover.setVisibility(View.VISIBLE);
- // This FrameLayout holds all the visible views in the current list or grid. We use this as
- // the starting Scene of the Transition later.
- FrameLayout before = copyVisibleViews();
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
- mCover.addView(before, params);
- // Swap the actual list.
- swapAbsListView();
- // We also swap the icon for the toggle button.
- ActivityCompat.invalidateOptionsMenu(getActivity());
- // It is now ready to start the transition.
- mAbsListView.post(new Runnable() {
- @Override
- public void run() {
- // BEGIN_INCLUDE(transition_with_listener)
- Scene scene = new Scene(mCover, copyVisibleViews());
- Transition transition = new AutoTransition();
- transition.addListener(AdapterTransitionFragment.this);
- TransitionManager.go(scene, transition);
- // END_INCLUDE(transition_with_listener)
- }
- });
- }
-
- /**
- * Swap ListView with GridView, or GridView with ListView.
- */
- private void swapAbsListView() {
- // We save the current scrolling position before removing the current list.
- int first = mAbsListView.getFirstVisiblePosition();
- // If the current list is a GridView, we replace it with a ListView. If it is a ListView,
- // a GridView.
- LayoutInflater inflater = LayoutInflater.from(getActivity());
- inflateAbsList(inflater, (ViewGroup) mAbsListView.getParent(),
- mAbsListView instanceof GridView);
- mAbsListView.setAdapter(mAdapter);
- // We restore the scrolling position here.
- mAbsListView.setSelection(first);
- // The new list is ready, and we replace the existing one with it.
- mContent.removeAllViews();
- mContent.addView(mAbsListView);
- }
-
- /**
- * Copy all the visible views in the mAbsListView into a new FrameLayout and return it.
- *
- * @return a FrameLayout with all the visible views inside.
- */
- private FrameLayout copyVisibleViews() {
- // This is the FrameLayout we return afterwards.
- FrameLayout layout = new FrameLayout(getActivity());
- // The transition framework requires to set ID for all views to be animated.
- layout.setId(ROOT_ID);
- // We only copy visible views.
- int first = mAbsListView.getFirstVisiblePosition();
- int index = 0;
- while (true) {
- // This is one of the views that we copy. Note that the argument for getChildAt is a
- // zero-oriented index, and it doesn't usually match with its position in the list.
- View source = mAbsListView.getChildAt(index);
- if (null == source) {
- break;
- }
- // This is the copy of the original view.
- View destination = mAdapter.getView(first + index, null, layout);
- assert destination != null;
- destination.setId(ROOT_ID + first + index);
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- source.getWidth(), source.getHeight());
- params.leftMargin = (int) source.getX();
- params.topMargin = (int) source.getY();
- layout.addView(destination, params);
- ++index;
- }
- return layout;
- }
-
-}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java
deleted file mode 100644
index bca1c5f..0000000
--- a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/Meat.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 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.adaptertransition;
-
-/**
- * Sample data.
- */
-public class Meat {
-
- public int resourceId;
- public String title;
-
- public Meat(int resourceId, String title) {
- this.resourceId = resourceId;
- this.title = title;
- }
-
- public static final Meat[] MEATS = {
- new Meat(R.drawable.p1, "First"),
- new Meat(R.drawable.p2, "Second"),
- new Meat(R.drawable.p3, "Third"),
- new Meat(R.drawable.p4, "Fourth"),
- new Meat(R.drawable.p5, "Fifth"),
- new Meat(R.drawable.p6, "Sixth"),
- new Meat(R.drawable.p7, "Seventh"),
- new Meat(R.drawable.p8, "Eighth"),
- new Meat(R.drawable.p9, "Ninth"),
- new Meat(R.drawable.p10, "Tenth"),
- new Meat(R.drawable.p11, "Eleventh"),
- };
-
-}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java b/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java
deleted file mode 100644
index dea4435..0000000
--- a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MeatAdapter.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2014 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.adaptertransition;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-/**
- * This class provides data as Views. It is designed to support both ListView and GridView by
- * changing a layout resource file to inflate.
- */
-public class MeatAdapter extends BaseAdapter implements AbsListView.OnItemClickListener {
-
- private final LayoutInflater mLayoutInflater;
- private final int mResourceId;
-
- /**
- * Create a new instance of {@link MeatAdapter}.
- *
- * @param inflater The layout inflater.
- * @param resourceId The resource ID for the layout to be used. The layout should contain an
- * ImageView with ID of "meat_image" and a TextView with ID of "meat_title".
- */
- public MeatAdapter(LayoutInflater inflater, int resourceId) {
- mLayoutInflater = inflater;
- mResourceId = resourceId;
- }
-
- @Override
- public int getCount() {
- return Meat.MEATS.length;
- }
-
- @Override
- public Meat getItem(int position) {
- return Meat.MEATS[position];
- }
-
- @Override
- public long getItemId(int position) {
- return Meat.MEATS[position].resourceId;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final View view;
- final ViewHolder holder;
- if (null == convertView) {
- view = mLayoutInflater.inflate(mResourceId, parent, false);
- holder = new ViewHolder();
- assert view != null;
- holder.image = (ImageView) view.findViewById(R.id.meat_image);
- holder.title = (TextView) view.findViewById(R.id.meat_title);
- view.setTag(holder);
- } else {
- view = convertView;
- holder = (ViewHolder) view.getTag();
- }
- Meat meat = getItem(position);
- holder.image.setImageResource(meat.resourceId);
- holder.title.setText(meat.title);
- return view;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- ViewHolder holder = (ViewHolder) view.getTag();
- Context context = view.getContext();
- if (null != holder && null != holder.title && null != context) {
- Toast.makeText(context, context.getString(R.string.item_clicked,
- holder.title.getText()), Toast.LENGTH_SHORT).show();
- }
- }
-
- private static class ViewHolder {
- public ImageView image;
- public TextView title;
- }
-
-}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java
deleted file mode 100644
index 20049e3..0000000
--- a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabLayout.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * 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.
- */
-
-package com.example.android.common.view;
-
-import android.content.Context;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.HorizontalScrollView;
-import android.widget.TextView;
-
-/**
- * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
- * the user's scroll progress.
- * <p>
- * To use the component, simply add it to your view hierarchy. Then in your
- * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
- * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
- * <p>
- * The colors can be customized in two ways. The first and simplest is to provide an array of colors
- * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
- * alternative is via the {@link TabColorizer} interface which provides you complete control over
- * which color is used for any individual position.
- * <p>
- * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
- * providing the layout ID of your custom layout.
- */
-public class SlidingTabLayout extends HorizontalScrollView {
-
- /**
- * Allows complete control over the colors drawn in the tab layout. Set with
- * {@link #setCustomTabColorizer(TabColorizer)}.
- */
- public interface TabColorizer {
-
- /**
- * @return return the color of the indicator used when {@code position} is selected.
- */
- int getIndicatorColor(int position);
-
- /**
- * @return return the color of the divider drawn to the right of {@code position}.
- */
- int getDividerColor(int position);
-
- }
-
- private static final int TITLE_OFFSET_DIPS = 24;
- private static final int TAB_VIEW_PADDING_DIPS = 16;
- private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
-
- private int mTitleOffset;
-
- private int mTabViewLayoutId;
- private int mTabViewTextViewId;
-
- private ViewPager mViewPager;
- private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
-
- private final SlidingTabStrip mTabStrip;
-
- public SlidingTabLayout(Context context) {
- this(context, null);
- }
-
- public SlidingTabLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- // Disable the Scroll Bar
- setHorizontalScrollBarEnabled(false);
- // Make sure that the Tab Strips fills this View
- setFillViewport(true);
-
- mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
-
- mTabStrip = new SlidingTabStrip(context);
- addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
-
- /**
- * Set the custom {@link TabColorizer} to be used.
- *
- * If you only require simple custmisation then you can use
- * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
- * similar effects.
- */
- public void setCustomTabColorizer(TabColorizer tabColorizer) {
- mTabStrip.setCustomTabColorizer(tabColorizer);
- }
-
- /**
- * Sets the colors to be used for indicating the selected tab. These colors are treated as a
- * circular array. Providing one color will mean that all tabs are indicated with the same color.
- */
- public void setSelectedIndicatorColors(int... colors) {
- mTabStrip.setSelectedIndicatorColors(colors);
- }
-
- /**
- * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
- * Providing one color will mean that all tabs are indicated with the same color.
- */
- public void setDividerColors(int... colors) {
- mTabStrip.setDividerColors(colors);
- }
-
- /**
- * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
- * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
- * that the layout can update it's scroll position correctly.
- *
- * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
- */
- public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
- mViewPagerPageChangeListener = listener;
- }
-
- /**
- * Set the custom layout to be inflated for the tab views.
- *
- * @param layoutResId Layout id to be inflated
- * @param textViewId id of the {@link TextView} in the inflated view
- */
- public void setCustomTabView(int layoutResId, int textViewId) {
- mTabViewLayoutId = layoutResId;
- mTabViewTextViewId = textViewId;
- }
-
- /**
- * Sets the associated view pager. Note that the assumption here is that the pager content
- * (number of tabs and tab titles) does not change after this call has been made.
- */
- public void setViewPager(ViewPager viewPager) {
- mTabStrip.removeAllViews();
-
- mViewPager = viewPager;
- if (viewPager != null) {
- viewPager.setOnPageChangeListener(new InternalViewPagerListener());
- populateTabStrip();
- }
- }
-
- /**
- * Create a default view to be used for tabs. This is called if a custom tab view is not set via
- * {@link #setCustomTabView(int, int)}.
- */
- protected TextView createDefaultTabView(Context context) {
- TextView textView = new TextView(context);
- textView.setGravity(Gravity.CENTER);
- textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
- textView.setTypeface(Typeface.DEFAULT_BOLD);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- // If we're running on Honeycomb or newer, then we can use the Theme's
- // selectableItemBackground to ensure that the View has a pressed state
- TypedValue outValue = new TypedValue();
- getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
- outValue, true);
- textView.setBackgroundResource(outValue.resourceId);
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
- textView.setAllCaps(true);
- }
-
- int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
- textView.setPadding(padding, padding, padding, padding);
-
- return textView;
- }
-
- private void populateTabStrip() {
- final PagerAdapter adapter = mViewPager.getAdapter();
- final View.OnClickListener tabClickListener = new TabClickListener();
-
- for (int i = 0; i < adapter.getCount(); i++) {
- View tabView = null;
- TextView tabTitleView = null;
-
- if (mTabViewLayoutId != 0) {
- // If there is a custom tab view layout id set, try and inflate it
- tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
- false);
- tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
- }
-
- if (tabView == null) {
- tabView = createDefaultTabView(getContext());
- }
-
- if (tabTitleView == null && TextView.class.isInstance(tabView)) {
- tabTitleView = (TextView) tabView;
- }
-
- tabTitleView.setText(adapter.getPageTitle(i));
- tabView.setOnClickListener(tabClickListener);
-
- mTabStrip.addView(tabView);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mViewPager != null) {
- scrollToTab(mViewPager.getCurrentItem(), 0);
- }
- }
-
- private void scrollToTab(int tabIndex, int positionOffset) {
- final int tabStripChildCount = mTabStrip.getChildCount();
- if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
- return;
- }
-
- View selectedChild = mTabStrip.getChildAt(tabIndex);
- if (selectedChild != null) {
- int targetScrollX = selectedChild.getLeft() + positionOffset;
-
- if (tabIndex > 0 || positionOffset > 0) {
- // If we're not at the first child and are mid-scroll, make sure we obey the offset
- targetScrollX -= mTitleOffset;
- }
-
- scrollTo(targetScrollX, 0);
- }
- }
-
- private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
- private int mScrollState;
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- int tabStripChildCount = mTabStrip.getChildCount();
- if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
- return;
- }
-
- mTabStrip.onViewPagerPageChanged(position, positionOffset);
-
- View selectedTitle = mTabStrip.getChildAt(position);
- int extraOffset = (selectedTitle != null)
- ? (int) (positionOffset * selectedTitle.getWidth())
- : 0;
- scrollToTab(position, extraOffset);
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
- positionOffsetPixels);
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mScrollState = state;
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageScrollStateChanged(state);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
- mTabStrip.onViewPagerPageChanged(position, 0f);
- scrollToTab(position, 0);
- }
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageSelected(position);
- }
- }
-
- }
-
- private class TabClickListener implements View.OnClickListener {
- @Override
- public void onClick(View v) {
- for (int i = 0; i < mTabStrip.getChildCount(); i++) {
- if (v == mTabStrip.getChildAt(i)) {
- mViewPager.setCurrentItem(i);
- return;
- }
- }
- }
- }
-
-}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java
deleted file mode 100644
index d5bbbae..0000000
--- a/samples/browseable/AdapterTransition/src/com.example.android.common/view/SlidingTabStrip.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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.
- */
-
-package com.example.android.common.view;
-
-import android.R;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.LinearLayout;
-
-class SlidingTabStrip extends LinearLayout {
-
- private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
- private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
- private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
- private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
-
- private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
- private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
- private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
-
- private final int mBottomBorderThickness;
- private final Paint mBottomBorderPaint;
-
- private final int mSelectedIndicatorThickness;
- private final Paint mSelectedIndicatorPaint;
-
- private final int mDefaultBottomBorderColor;
-
- private final Paint mDividerPaint;
- private final float mDividerHeight;
-
- private int mSelectedPosition;
- private float mSelectionOffset;
-
- private SlidingTabLayout.TabColorizer mCustomTabColorizer;
- private final SimpleTabColorizer mDefaultTabColorizer;
-
- SlidingTabStrip(Context context) {
- this(context, null);
- }
-
- SlidingTabStrip(Context context, AttributeSet attrs) {
- super(context, attrs);
- setWillNotDraw(false);
-
- final float density = getResources().getDisplayMetrics().density;
-
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
- final int themeForegroundColor = outValue.data;
-
- mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
- DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
-
- mDefaultTabColorizer = new SimpleTabColorizer();
- mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
- mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
- DEFAULT_DIVIDER_COLOR_ALPHA));
-
- mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
- mBottomBorderPaint = new Paint();
- mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
-
- mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
- mSelectedIndicatorPaint = new Paint();
-
- mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
- mDividerPaint = new Paint();
- mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
- }
-
- void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
- mCustomTabColorizer = customTabColorizer;
- invalidate();
- }
-
- void setSelectedIndicatorColors(int... colors) {
- // Make sure that the custom colorizer is removed
- mCustomTabColorizer = null;
- mDefaultTabColorizer.setIndicatorColors(colors);
- invalidate();
- }
-
- void setDividerColors(int... colors) {
- // Make sure that the custom colorizer is removed
- mCustomTabColorizer = null;
- mDefaultTabColorizer.setDividerColors(colors);
- invalidate();
- }
-
- void onViewPagerPageChanged(int position, float positionOffset) {
- mSelectedPosition = position;
- mSelectionOffset = positionOffset;
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- final int height = getHeight();
- final int childCount = getChildCount();
- final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
- final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
- ? mCustomTabColorizer
- : mDefaultTabColorizer;
-
- // Thick colored underline below the current selection
- if (childCount > 0) {
- View selectedTitle = getChildAt(mSelectedPosition);
- int left = selectedTitle.getLeft();
- int right = selectedTitle.getRight();
- int color = tabColorizer.getIndicatorColor(mSelectedPosition);
-
- if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
- int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
- if (color != nextColor) {
- color = blendColors(nextColor, color, mSelectionOffset);
- }
-
- // Draw the selection partway between the tabs
- View nextTitle = getChildAt(mSelectedPosition + 1);
- left = (int) (mSelectionOffset * nextTitle.getLeft() +
- (1.0f - mSelectionOffset) * left);
- right = (int) (mSelectionOffset * nextTitle.getRight() +
- (1.0f - mSelectionOffset) * right);
- }
-
- mSelectedIndicatorPaint.setColor(color);
-
- canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
- height, mSelectedIndicatorPaint);
- }
-
- // Thin underline along the entire bottom edge
- canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
-
- // Vertical separators between the titles
- int separatorTop = (height - dividerHeightPx) / 2;
- for (int i = 0; i < childCount - 1; i++) {
- View child = getChildAt(i);
- mDividerPaint.setColor(tabColorizer.getDividerColor(i));
- canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
- separatorTop + dividerHeightPx, mDividerPaint);
- }
- }
-
- /**
- * Set the alpha value of the {@code color} to be the given {@code alpha} value.
- */
- private static int setColorAlpha(int color, byte alpha) {
- return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
- }
-
- /**
- * Blend {@code color1} and {@code color2} using the given ratio.
- *
- * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
- * 0.0 will return {@code color2}.
- */
- private static int blendColors(int color1, int color2, float ratio) {
- final float inverseRation = 1f - ratio;
- float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
- float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
- float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
- return Color.rgb((int) r, (int) g, (int) b);
- }
-
- private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
- private int[] mIndicatorColors;
- private int[] mDividerColors;
-
- @Override
- public final int getIndicatorColor(int position) {
- return mIndicatorColors[position % mIndicatorColors.length];
- }
-
- @Override
- public final int getDividerColor(int position) {
- return mDividerColors[position % mDividerColors.length];
- }
-
- void setIndicatorColors(int... colors) {
- mIndicatorColors = colors;
- }
-
- void setDividerColors(int... colors) {
- mDividerColors = colors;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/browseable/AgendaData/Application/res/layout/activity_main.xml b/samples/browseable/AgendaData/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/AgendaData/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/AppRestrictionEnforcer/res/layout/activity_main.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/AppRestrictionEnforcer/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml
index e6c50a2..0118191 100644
--- a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml
+++ b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml
@@ -14,31 +14,111 @@
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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingBottom="@dimen/vertical_page_margin"
- android:paddingLeft="@dimen/horizontal_page_margin"
- android:paddingRight="@dimen/horizontal_page_margin"
- android:paddingTop="@dimen/vertical_page_margin">
- <TextView
- android:id="@+id/status"
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/status_not_installed" />
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/vertical_page_margin"
+ android:paddingLeft="@dimen/horizontal_page_margin"
+ android:paddingRight="@dimen/horizontal_page_margin"
+ android:paddingTop="@dimen/vertical_page_margin">
- <Button
- android:id="@+id/unhide"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/unhide" />
- <Switch
- android:id="@+id/say_hello"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/allow_saying_hello" />
+ <Switch
+ android:id="@+id/say_hello"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/allow_saying_hello"/>
-</LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/vertical_page_margin"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:labelFor="@id/message"
+ android:text="@string/message"/>
+
+ <EditText
+ android:id="@id/message"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="text"
+ android:maxLines="1"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/vertical_page_margin"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:labelFor="@id/number"
+ android:text="@string/number"/>
+
+ <EditText
+ android:id="@id/number"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="number"
+ android:maxLines="1"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/vertical_page_margin"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/rank"/>
+
+ <Spinner
+ android:id="@+id/rank"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/margin_small"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/vertical_page_margin"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/approvals"/>
+
+ <LinearLayout
+ android:id="@+id/approvals"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/margin_small"
+ android:orientation="vertical"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml
index e9e9fe8..8fde91f 100644
--- a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml
+++ b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml
@@ -20,7 +20,7 @@
android:layout_height="match_parent"
tools:context="com.example.android.basicmanagedprofile.MainActivity.MainFragment">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
diff --git a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_status.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_status.xml
new file mode 100644
index 0000000..a2d60eb
--- /dev/null
+++ b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_status.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/vertical_page_margin"
+ android:paddingLeft="@dimen/horizontal_page_margin"
+ android:paddingRight="@dimen/horizontal_page_margin"
+ android:paddingTop="@dimen/vertical_page_margin">
+
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/status_not_installed"/>
+
+ <Button
+ android:id="@+id/unhide"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/unhide"/>
+
+</LinearLayout>
diff --git a/samples/browseable/AdapterTransition/res/values/strings.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/separator.xml
similarity index 62%
copy from samples/browseable/AdapterTransition/res/values/strings.xml
copy to samples/browseable/AppRestrictionEnforcer/res/layout/separator.xml
index 02b87cf..6927d80 100644
--- a/samples/browseable/AdapterTransition/res/values/strings.xml
+++ b/samples/browseable/AppRestrictionEnforcer/res/layout/separator.xml
@@ -6,7 +6,7 @@
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
+ 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,
@@ -14,8 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <string name="item_clicked">%s clicked</string>
- <string name="show_as_grid">Show as grid</string>
- <string name="show_as_list">Show as list</string>
-</resources>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginBottom="@dimen/margin_medium"
+ android:layout_marginTop="@dimen/margin_medium"
+ android:background="#9000"/>
diff --git a/samples/browseable/AdapterTransition/res/values/strings.xml b/samples/browseable/AppRestrictionEnforcer/res/values/ids.xml
similarity index 74%
copy from samples/browseable/AdapterTransition/res/values/strings.xml
copy to samples/browseable/AppRestrictionEnforcer/res/values/ids.xml
index 02b87cf..04ba4ec 100644
--- a/samples/browseable/AdapterTransition/res/values/strings.xml
+++ b/samples/browseable/AppRestrictionEnforcer/res/values/ids.xml
@@ -6,7 +6,7 @@
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
+ 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,
@@ -15,7 +15,7 @@
limitations under the License.
-->
<resources>
- <string name="item_clicked">%s clicked</string>
- <string name="show_as_grid">Show as grid</string>
- <string name="show_as_list">Show as list</string>
+ <item name="message" type="id"/>
+ <item name="number" type="id"/>
+ <item name="approval" type="id"/>
</resources>
diff --git a/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml b/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml
index 3029e04..e35daee 100644
--- a/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml
+++ b/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml
@@ -25,4 +25,8 @@
<string name="allowed">Allowed</string>
<string name="disallowed">Disallowed</string>
<string name="profile_name">AppRestrictionEnforcer </string>
+ <string name="message">Message: </string>
+ <string name="number">Number: </string>
+ <string name="rank">Rank: </string>
+ <string name="approvals">Approvals: </string>
</resources>
diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java
index 6db54f6..8b0620f 100644
--- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java
+++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java
@@ -22,34 +22,34 @@
import android.content.RestrictionEntry;
import android.content.RestrictionsManager;
import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
import android.widget.Switch;
-import android.widget.TextView;
import android.widget.Toast;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
* This fragment provides UI and functionality to set restrictions on the AppRestrictionSchema
* sample.
*/
-public class AppRestrictionEnforcerFragment extends Fragment implements View.OnClickListener,
- CompoundButton.OnCheckedChangeListener {
-
- /**
- * Package name of the AppRestrictionSchema sample.
- */
- private static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
- = "com.example.android.apprestrictionschema";
+public class AppRestrictionEnforcerFragment extends Fragment implements
+ CompoundButton.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
/**
* Key for {@link SharedPreferences}
@@ -62,15 +62,38 @@
private static final String RESTRICTION_KEY_SAY_HELLO = "can_say_hello";
/**
- * Default boolean value for "can_say_hello" restriction. The actual value is loaded in
- * {@link #loadRestrictions(android.app.Activity)}.
+ * Key for the string restriction in AppRestrictionSchema.
*/
- private boolean mDefaultValueRestrictionSayHello;
+ private static final String RESTRICTION_KEY_MESSAGE = "message";
+
+ /**
+ * Key for the integer restriction in AppRestrictionSchema.
+ */
+ private static final String RESTRICTION_KEY_NUMBER = "number";
+
+ /**
+ * Key for the choice restriction in AppRestrictionSchema.
+ */
+ private static final String RESTRICTION_KEY_RANK = "rank";
+
+ /**
+ * Key for the multi-select restriction in AppRestrictionSchema.
+ */
+ private static final String RESTRICTION_KEY_APPROVALS = "approvals";
+
+ private static final String DELIMETER = ",";
+
+ /**
+ * Current status of the restrictions.
+ */
+ private Bundle mCurrentRestrictions = new Bundle();
// UI Components
- private TextView mTextStatus;
- private Button mButtonUnhide;
private Switch mSwitchSayHello;
+ private EditText mEditMessage;
+ private EditText mEditNumber;
+ private Spinner mSpinnerRank;
+ private LinearLayout mLayoutApprovals;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -80,99 +103,72 @@
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
- mTextStatus = (TextView) view.findViewById(R.id.status);
- mButtonUnhide = (Button) view.findViewById(R.id.unhide);
+ // Retain references for the UI elements
mSwitchSayHello = (Switch) view.findViewById(R.id.say_hello);
- mButtonUnhide.setOnClickListener(this);
- mSwitchSayHello.setOnCheckedChangeListener(this);
+ mEditMessage = (EditText) view.findViewById(R.id.message);
+ mEditNumber = (EditText) view.findViewById(R.id.number);
+ mSpinnerRank = (Spinner) view.findViewById(R.id.rank);
+ mLayoutApprovals = (LinearLayout) view.findViewById(R.id.approvals);
}
@Override
public void onResume() {
super.onResume();
- updateUi(getActivity());
- }
-
- @Override
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.unhide: {
- unhideApp(getActivity());
- break;
- }
- }
+ loadRestrictions(getActivity());
}
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
switch (compoundButton.getId()) {
case R.id.say_hello: {
- allowSayHello(getActivity(), checked);
+ saveCanSayHello(getActivity(), checked);
+ break;
+ }
+ case R.id.approval: {
+ if (checked) {
+ addApproval(getActivity(), (String) compoundButton.getTag());
+ } else {
+ removeApproval(getActivity(), (String) compoundButton.getTag());
+ }
break;
}
}
}
- /**
- * Updates the UI components according to the current status of AppRestrictionSchema and its
- * restriction.
- *
- * @param activity The activity
- */
- private void updateUi(Activity activity) {
- PackageManager packageManager = activity.getPackageManager();
- try {
- ApplicationInfo info = packageManager.getApplicationInfo(
- PACKAGE_NAME_APP_RESTRICTION_SCHEMA, PackageManager.GET_UNINSTALLED_PACKAGES);
- DevicePolicyManager devicePolicyManager =
- (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
- if (0 < (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
- if (!devicePolicyManager.isApplicationHidden(
- EnforcerDeviceAdminReceiver.getComponentName(activity),
- PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
- // The app is ready
- loadRestrictions(activity);
- mTextStatus.setVisibility(View.GONE);
- mButtonUnhide.setVisibility(View.GONE);
- mSwitchSayHello.setVisibility(View.VISIBLE);
- mSwitchSayHello.setOnCheckedChangeListener(null);
- mSwitchSayHello.setChecked(canSayHello(activity));
- mSwitchSayHello.setOnCheckedChangeListener(this);
- } else {
- // The app is installed but hidden in this profile
- mTextStatus.setText(R.string.status_not_activated);
- mTextStatus.setVisibility(View.VISIBLE);
- mButtonUnhide.setVisibility(View.VISIBLE);
- mSwitchSayHello.setVisibility(View.GONE);
+ private TextWatcher mWatcherMessage = new EasyTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ saveMessage(getActivity(), s.toString());
+ }
+ };
+
+ private TextWatcher mWatcherNumber = new EasyTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ try {
+ String string = s.toString();
+ if (!TextUtils.isEmpty(string)) {
+ saveNumber(getActivity(), Integer.parseInt(string));
}
- } else {
- // Need to reinstall the sample app
- mTextStatus.setText(R.string.status_need_reinstall);
- mTextStatus.setVisibility(View.VISIBLE);
- mButtonUnhide.setVisibility(View.GONE);
- mSwitchSayHello.setVisibility(View.GONE);
+ } catch (NumberFormatException e) {
+ Toast.makeText(getActivity(), "Not an integer!", Toast.LENGTH_SHORT).show();
}
- } catch (PackageManager.NameNotFoundException e) {
- mTextStatus.setText(R.string.status_not_installed);
- mTextStatus.setVisibility(View.VISIBLE);
- mButtonUnhide.setVisibility(View.GONE);
- mSwitchSayHello.setVisibility(View.GONE);
+ }
+ };
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ switch (parent.getId()) {
+ case R.id.rank: {
+ saveRank(getActivity(), (String) parent.getAdapter().getItem(position));
+ break;
+ }
}
}
- /**
- * Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
- *
- * @param activity The activity
- */
- private void unhideApp(Activity activity) {
- DevicePolicyManager devicePolicyManager =
- (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
- devicePolicyManager.setApplicationHidden(
- EnforcerDeviceAdminReceiver.getComponentName(activity),
- PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
- Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
- updateUi(activity);
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Nothing to do
}
/**
@@ -182,50 +178,187 @@
* @param activity The activity
*/
private void loadRestrictions(Activity activity) {
- RestrictionsManager restrictionsManager =
+ RestrictionsManager manager =
(RestrictionsManager) activity.getSystemService(Context.RESTRICTIONS_SERVICE);
List<RestrictionEntry> restrictions =
- restrictionsManager.getManifestRestrictions(PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
+ manager.getManifestRestrictions(Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA);
+ SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
for (RestrictionEntry restriction : restrictions) {
- if (RESTRICTION_KEY_SAY_HELLO.equals(restriction.getKey())) {
- mDefaultValueRestrictionSayHello = restriction.getSelectedState();
+ String key = restriction.getKey();
+ if (RESTRICTION_KEY_SAY_HELLO.equals(key)) {
+ updateCanSayHello(prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO,
+ restriction.getSelectedState()));
+ } else if (RESTRICTION_KEY_MESSAGE.equals(key)) {
+ updateMessage(prefs.getString(RESTRICTION_KEY_MESSAGE,
+ restriction.getSelectedString()));
+ } else if (RESTRICTION_KEY_NUMBER.equals(key)) {
+ updateNumber(prefs.getInt(RESTRICTION_KEY_NUMBER,
+ restriction.getIntValue()));
+ } else if (RESTRICTION_KEY_RANK.equals(key)) {
+ updateRank(activity, restriction.getChoiceValues(),
+ prefs.getString(RESTRICTION_KEY_RANK, restriction.getSelectedString()));
+ } else if (RESTRICTION_KEY_APPROVALS.equals(key)) {
+ updateApprovals(activity, restriction.getChoiceValues(),
+ TextUtils.split(prefs.getString(RESTRICTION_KEY_APPROVALS,
+ TextUtils.join(DELIMETER,
+ restriction.getAllSelectedStrings())),
+ DELIMETER));
}
}
}
- /**
- * Returns whether the AppRestrictionSchema is currently allowed to say hello to its user. Note
- * that a profile/device owner needs to remember each restriction value on its own.
- *
- * @param activity The activity
- * @return True if the AppRestrictionSchema is allowed to say hello
- */
- private boolean canSayHello(Activity activity) {
- SharedPreferences prefs = activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
- return prefs.getBoolean(RESTRICTION_KEY_SAY_HELLO, mDefaultValueRestrictionSayHello);
+ private void updateCanSayHello(boolean canSayHello) {
+ mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, canSayHello);
+ mSwitchSayHello.setOnCheckedChangeListener(null);
+ mSwitchSayHello.setChecked(canSayHello);
+ mSwitchSayHello.setOnCheckedChangeListener(this);
+ }
+
+ private void updateMessage(String message) {
+ mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
+ mEditMessage.removeTextChangedListener(mWatcherMessage);
+ mEditMessage.setText(message);
+ mEditMessage.addTextChangedListener(mWatcherMessage);
+ }
+
+ private void updateNumber(int number) {
+ mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
+ mEditNumber.removeTextChangedListener(mWatcherNumber);
+ mEditNumber.setText(String.valueOf(number));
+ mEditNumber.addTextChangedListener(mWatcherNumber);
+ }
+
+ private void updateRank(Context context, String[] ranks, String selectedRank) {
+ mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, selectedRank);
+ mSpinnerRank.setAdapter(new ArrayAdapter<>(context,
+ android.R.layout.simple_spinner_dropdown_item, ranks));
+ mSpinnerRank.setSelection(search(ranks, selectedRank));
+ mSpinnerRank.setOnItemSelectedListener(this);
+ }
+
+ private void updateApprovals(Context context, String[] approvals,
+ String[] selectedApprovals) {
+ mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, selectedApprovals);
+ mLayoutApprovals.removeAllViews();
+ for (String approval : approvals) {
+ Switch sw = new Switch(context);
+ sw.setText(approval);
+ sw.setTag(approval);
+ sw.setChecked(Arrays.asList(selectedApprovals).contains(approval));
+ sw.setOnCheckedChangeListener(this);
+ sw.setId(R.id.approval);
+ mLayoutApprovals.addView(sw);
+ }
}
/**
- * Sets the value for the "cay_say_hello" restriction of AppRestrictionSchema.
+ * Saves the value for the "cay_say_hello" restriction of AppRestrictionSchema.
*
* @param activity The activity
* @param allow The value to be set for the restriction.
*/
- private void allowSayHello(Activity activity, boolean allow) {
+ private void saveCanSayHello(Activity activity, boolean allow) {
+ mCurrentRestrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
+ saveRestrictions(activity);
+ // Note that the owner app needs to remember the restrictions on its own.
+ editPreferences(activity).putBoolean(RESTRICTION_KEY_SAY_HELLO, allow).apply();
+ }
+
+ /**
+ * Saves the value for the "message" restriction of AppRestrictionSchema.
+ *
+ * @param activity The activity
+ * @param message The value to be set for the restriction.
+ */
+ private void saveMessage(Activity activity, String message) {
+ mCurrentRestrictions.putString(RESTRICTION_KEY_MESSAGE, message);
+ saveRestrictions(activity);
+ editPreferences(activity).putString(RESTRICTION_KEY_MESSAGE, message).apply();
+ }
+
+ /**
+ * Saves the value for the "number" restriction of AppRestrictionSchema.
+ *
+ * @param activity The activity
+ * @param number The value to be set for the restriction.
+ */
+ private void saveNumber(Activity activity, int number) {
+ mCurrentRestrictions.putInt(RESTRICTION_KEY_NUMBER, number);
+ saveRestrictions(activity);
+ editPreferences(activity).putInt(RESTRICTION_KEY_NUMBER, number).apply();
+ }
+
+ /**
+ * Saves the value for the "rank" restriction of AppRestrictionSchema.
+ *
+ * @param activity The activity
+ * @param rank The value to be set for the restriction.
+ */
+ private void saveRank(Activity activity, String rank) {
+ mCurrentRestrictions.putString(RESTRICTION_KEY_RANK, rank);
+ saveRestrictions(activity);
+ editPreferences(activity).putString(RESTRICTION_KEY_RANK, rank).apply();
+ }
+
+ private void addApproval(Activity activity, String approval) {
+ List<String> approvals = new ArrayList<>(Arrays.asList(
+ mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
+ if (approvals.contains(approval)) {
+ return;
+ }
+ approvals.add(approval);
+ saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
+ }
+
+ private void removeApproval(Activity activity, String approval) {
+ List<String> approvals = new ArrayList<>(Arrays.asList(
+ mCurrentRestrictions.getStringArray(RESTRICTION_KEY_APPROVALS)));
+ if (!approval.contains(approval)) {
+ return;
+ }
+ approvals.remove(approval);
+ saveApprovals(activity, approvals.toArray(new String[approvals.size()]));
+ }
+
+ /**
+ * Saves the value for the "approvals" restriction of AppRestrictionSchema.
+ *
+ * @param activity The activity
+ * @param approvals The value to be set for the restriction.
+ */
+ private void saveApprovals(Activity activity, String[] approvals) {
+ mCurrentRestrictions.putStringArray(RESTRICTION_KEY_APPROVALS, approvals);
+ saveRestrictions(activity);
+ editPreferences(activity).putString(RESTRICTION_KEY_APPROVALS,
+ TextUtils.join(DELIMETER, approvals)).apply();
+ }
+
+ private void saveRestrictions(Activity activity) {
DevicePolicyManager devicePolicyManager
= (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
- Bundle restrictions = new Bundle();
- restrictions.putBoolean(RESTRICTION_KEY_SAY_HELLO, allow);
devicePolicyManager.setApplicationRestrictions(
EnforcerDeviceAdminReceiver.getComponentName(activity),
- PACKAGE_NAME_APP_RESTRICTION_SCHEMA, restrictions);
- // The profile/device owner needs to remember the current state of restrictions on its own
- activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE)
- .edit()
- .putBoolean(RESTRICTION_KEY_SAY_HELLO, allow)
- .apply();
- Toast.makeText(activity, allow ? R.string.allowed : R.string.disallowed,
- Toast.LENGTH_SHORT).show();
+ Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, mCurrentRestrictions);
+ }
+
+ private SharedPreferences.Editor editPreferences(Activity activity) {
+ return activity.getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE).edit();
+ }
+
+ /**
+ * Sequential search
+ *
+ * @param array The string array
+ * @param s The string to search for
+ * @return Index if found. -1 if not found.
+ */
+ private int search(String[] array, String s) {
+ for (int i = 0; i < array.length; ++i) {
+ if (s.equals(array[i])) {
+ return i;
+ }
+ }
+ return -1;
}
}
diff --git a/samples/browseable/EmbeddedApp/Wearable/src/com.example.android.wearable.embeddedapp/WearableActivity.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/Constants.java
similarity index 65%
rename from samples/browseable/EmbeddedApp/Wearable/src/com.example.android.wearable.embeddedapp/WearableActivity.java
rename to samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/Constants.java
index 3703f34..bb4e958 100644
--- a/samples/browseable/EmbeddedApp/Wearable/src/com.example.android.wearable.embeddedapp/WearableActivity.java
+++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/Constants.java
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.example.android.wearable.embeddedapp;
+package com.example.android.apprestrictionenforcer;
-import android.app.Activity;
-import android.os.Bundle;
+public interface Constants {
-public class WearableActivity extends Activity {
+ /**
+ * Package name of the AppRestrictionSchema sample.
+ */
+ public static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA
+ = "com.example.android.apprestrictionschema";
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_wearable);
- }
}
diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/EasyTextWatcher.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/EasyTextWatcher.java
new file mode 100644
index 0000000..8e0abb4
--- /dev/null
+++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/EasyTextWatcher.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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.apprestrictionenforcer;
+
+import android.text.TextWatcher;
+
+/**
+ * This is a wrapper around {@link TextWatcher} that overrides
+ * {@link TextWatcher#beforeTextChanged(CharSequence, int, int, int)} and
+ * {@link TextWatcher#onTextChanged(CharSequence, int, int, int)} with empty bodies.
+ */
+public abstract class EasyTextWatcher implements TextWatcher {
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Do nothing
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Do nothing
+ }
+
+}
diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java
index 72224e1..c6b012b 100644
--- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java
+++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java
@@ -18,24 +18,44 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
-public class MainActivity extends FragmentActivity {
+public class MainActivity extends FragmentActivity implements StatusFragment.StatusUpdatedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_real);
if (null == savedInstanceState) {
- DevicePolicyManager manager = (DevicePolicyManager)
- getSystemService(Context.DEVICE_POLICY_SERVICE);
- if (manager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
- // If the managed profile is already set up, we show the main screen.
- showMainFragment();
- } else {
- // If not, we show the set up screen.
+ DevicePolicyManager devicePolicyManager =
+ (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+ PackageManager packageManager = getPackageManager();
+ if (!devicePolicyManager.isProfileOwnerApp(getApplicationContext().getPackageName())) {
+ // If the managed profile is not yet set up, we show the setup screen.
showSetupProfile();
+ } else {
+ try {
+ ApplicationInfo info = packageManager.getApplicationInfo(
+ Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ if (0 == (info.flags & ApplicationInfo.FLAG_INSTALLED)) {
+ // Need to reinstall the sample app
+ showStatusProfile();
+ } else if (devicePolicyManager.isApplicationHidden(
+ EnforcerDeviceAdminReceiver.getComponentName(this),
+ Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
+ // The app is installed but hidden in this profile
+ showStatusProfile();
+ } else {
+ // Everything is clear; show the main screen
+ showMainFragment();
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ showStatusProfile();
+ }
}
}
}
@@ -46,10 +66,21 @@
.commit();
}
+ private void showStatusProfile() {
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.container, new StatusFragment())
+ .commit();
+ }
+
private void showMainFragment() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new AppRestrictionEnforcerFragment())
.commit();
}
+ @Override
+ public void onStatusUpdated() {
+ showMainFragment();
+ }
+
}
diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/StatusFragment.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/StatusFragment.java
new file mode 100644
index 0000000..f4a4eb7
--- /dev/null
+++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/StatusFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 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.apprestrictionenforcer;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Provides UI for enabling the target app in this profile. The status of the app can be
+ * uninstalled, hidden, or enabled depending on the situations. This fragment shows suitable
+ * controls for each status.
+ */
+public class StatusFragment extends Fragment implements View.OnClickListener {
+
+ private TextView mTextStatus;
+ private Button mButtonUnhide;
+ private StatusUpdatedListener mListener;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_status, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ mTextStatus = (TextView) view.findViewById(R.id.status);
+ mButtonUnhide = (Button) view.findViewById(R.id.unhide);
+ mButtonUnhide.setOnClickListener(this);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mListener = (StatusUpdatedListener) activity;
+ }
+
+ @Override
+ public void onDetach() {
+ mListener = null;
+ super.onDetach();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUi(getActivity());
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.unhide: {
+ unhideApp(getActivity());
+ break;
+ }
+ }
+ }
+
+ private void updateUi(Activity activity) {
+ PackageManager packageManager = activity.getPackageManager();
+ try {
+ ApplicationInfo info = packageManager.getApplicationInfo(
+ Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ DevicePolicyManager devicePolicyManager =
+ (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
+ if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ if (!devicePolicyManager.isApplicationHidden(
+ EnforcerDeviceAdminReceiver.getComponentName(activity),
+ Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA)) {
+ // The app is ready to enforce restrictions
+ // This is unlikely to happen in this sample as unhideApp() handles it.
+ mListener.onStatusUpdated();
+ } else {
+ // The app is installed but hidden in this profile
+ mTextStatus.setText(R.string.status_not_activated);
+ mButtonUnhide.setVisibility(View.VISIBLE);
+ }
+ } else {
+ // Need to reinstall the sample app
+ mTextStatus.setText(R.string.status_need_reinstall);
+ mButtonUnhide.setVisibility(View.GONE);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Need to reinstall the sample app
+ mTextStatus.setText(R.string.status_need_reinstall);
+ mButtonUnhide.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Unhides the AppRestrictionSchema sample in case it is hidden in this profile.
+ *
+ * @param activity The activity
+ */
+ private void unhideApp(Activity activity) {
+ DevicePolicyManager devicePolicyManager =
+ (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
+ devicePolicyManager.setApplicationHidden(
+ EnforcerDeviceAdminReceiver.getComponentName(activity),
+ Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, false);
+ Toast.makeText(activity, "Enabled the app", Toast.LENGTH_SHORT).show();
+ mListener.onStatusUpdated();
+ }
+
+ public interface StatusUpdatedListener {
+ public void onStatusUpdated();
+ }
+
+}
diff --git a/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml b/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml
index fc5e23d..18ca0a4 100644
--- a/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml
+++ b/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml
@@ -14,24 +14,60 @@
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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="@dimen/margin_medium">
- <TextView
- android:id="@+id/say_hello_explanation"
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/explanation_can_say_hello_true"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:orientation="vertical"
+ android:padding="@dimen/margin_medium">
- <Button
- android:id="@+id/say_hello"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/margin_medium"
- android:text="@string/action_can_say_hello" />
+ <TextView
+ android:id="@+id/say_hello_explanation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ tools:text="@string/explanation_can_say_hello_true"/>
-</LinearLayout>
+ <Button
+ android:id="@+id/say_hello"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/margin_medium"
+ android:text="@string/action_can_say_hello"/>
+
+ <include layout="@layout/separator"/>
+
+ <TextView
+ android:id="@+id/your_number"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ tools:text="@string/your_number"/>
+
+ <include layout="@layout/separator"/>
+
+ <TextView
+ android:id="@+id/your_rank"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ tools:text="@string/your_rank"/>
+
+ <include layout="@layout/separator"/>
+
+ <TextView
+ android:id="@+id/approvals_you_have"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ tools:text="@string/approvals_you_have"/>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/samples/browseable/AdapterTransition/res/values/strings.xml b/samples/browseable/AppRestrictionSchema/res/layout/separator.xml
similarity index 62%
copy from samples/browseable/AdapterTransition/res/values/strings.xml
copy to samples/browseable/AppRestrictionSchema/res/layout/separator.xml
index 02b87cf..6927d80 100644
--- a/samples/browseable/AdapterTransition/res/values/strings.xml
+++ b/samples/browseable/AppRestrictionSchema/res/layout/separator.xml
@@ -6,7 +6,7 @@
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
+ 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,
@@ -14,8 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <string name="item_clicked">%s clicked</string>
- <string name="show_as_grid">Show as grid</string>
- <string name="show_as_list">Show as list</string>
-</resources>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginBottom="@dimen/margin_medium"
+ android:layout_marginTop="@dimen/margin_medium"
+ android:background="#9000"/>
diff --git a/samples/browseable/AppRestrictionSchema/res/values/restriction_values.xml b/samples/browseable/AppRestrictionSchema/res/values/restriction_values.xml
new file mode 100644
index 0000000..558d097
--- /dev/null
+++ b/samples/browseable/AppRestrictionSchema/res/values/restriction_values.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2014 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>
+
+ <!-- Bool restriction -->
+ <string name="title_can_say_hello">Can say hello</string>
+ <string name="description_can_say_hello">Whether the app can say hello to the user</string>
+ <bool name="default_can_say_hello">false</bool>
+
+ <!-- String restriction -->
+ <string name="title_message">Message</string>
+ <string name="description_message">A message string to show</string>
+ <string name="default_message">Hello!</string>
+
+ <!-- Integer restriction -->
+ <string name="title_number">Number</string>
+ <string name="description_number">Sample integer value</string>
+ <integer name="default_number">32582657</integer>
+
+ <!-- Choice restriction -->
+ <string name="title_rank">Rank</string>
+ <string name="description_rank">Rank of the user</string>
+ <string-array name="entries_rank">
+ <item>Apprentice</item>
+ <item>Intermediate</item>
+ <item>Master</item>
+ </string-array>
+ <string name="entry_value_rank_apprentice">apprentice</string>
+ <string name="entry_value_rank_intermediate">intermediate</string>
+ <string name="entry_value_rank_master">master</string>
+ <string-array name="entry_values_rank">
+ <item>@string/entry_value_rank_apprentice</item>
+ <item>@string/entry_value_rank_intermediate</item>
+ <item>@string/entry_value_rank_master</item>
+ </string-array>
+ <string name="default_rank">@string/entry_value_rank_apprentice</string>
+
+ <!-- Multi-select restriction -->
+ <string name="title_approvals">Approvals</string>
+ <string name="description_approvals">Approvals</string>
+ <string-array name="entries_approvals">
+ <item>Read</item>
+ <item>Write</item>
+ <item>Execute</item>
+ </string-array>
+ <string name="entry_value_approvals_read">read</string>
+ <string name="entry_value_approvals_write">write</string>
+ <string name="entry_value_approvals_execute">execute</string>
+ <string-array name="entry_values_approvals">
+ <item>@string/entry_value_approvals_read</item>
+ <item>@string/entry_value_approvals_write</item>
+ <item>@string/entry_value_approvals_execute</item>
+ </string-array>
+ <string-array name="default_approvals">
+ <!-- Empty -->
+ </string-array>
+
+ <!-- Hidden restriction -->
+ <string name="title_secret_code">Secret code</string>
+ <string name="description_secret_code">This restriction is hidden and will not be shown to the administrator.</string>
+ <string name="default_secret_code">(Hidden restriction must have some default value)</string>
+
+</resources>
diff --git a/samples/browseable/AppRestrictionSchema/res/values/strings.xml b/samples/browseable/AppRestrictionSchema/res/values/strings.xml
index b8ef110..6dce123 100644
--- a/samples/browseable/AppRestrictionSchema/res/values/strings.xml
+++ b/samples/browseable/AppRestrictionSchema/res/values/strings.xml
@@ -16,11 +16,14 @@
-->
<resources>
- <string name="title_can_say_hello">Can say hello</string>
- <string name="description_can_say_hello">Whether the app can say hello to the user</string>
<string name="explanation_can_say_hello_true">I can say hello to you.</string>
<string name="explanation_can_say_hello_false">I am restricted from saying hello to you.</string>
<string name="action_can_say_hello">Say hello</string>
- <string name="message_hello">Hello!</string>
+ <string name="message">All I can say is \"%s\".</string>
-</resources>
\ No newline at end of file
+ <string name="your_number">Your number: %d</string>
+ <string name="your_rank">Your rank: %s</string>
+ <string name="approvals_you_have">Approvals you have: %s</string>
+ <string name="none">none</string>
+
+</resources>
diff --git a/samples/browseable/AppRestrictionSchema/res/xml/app_restrictions.xml b/samples/browseable/AppRestrictionSchema/res/xml/app_restrictions.xml
index 409527f..9e47f45 100644
--- a/samples/browseable/AppRestrictionSchema/res/xml/app_restrictions.xml
+++ b/samples/browseable/AppRestrictionSchema/res/xml/app_restrictions.xml
@@ -16,11 +16,55 @@
-->
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
+ <!--
+ Refer to the javadoc of RestrictionsManager for detail of this file.
+ https://developer.android.com/reference/android/content/RestrictionsManager.html
+ -->
+
<restriction
- android:defaultValue="false"
+ android:defaultValue="@bool/default_can_say_hello"
android:description="@string/description_can_say_hello"
android:key="can_say_hello"
android:restrictionType="bool"
- android:title="@string/title_can_say_hello" />
+ android:title="@string/title_can_say_hello"/>
+
+ <restriction
+ android:defaultValue="@string/default_message"
+ android:description="@string/description_message"
+ android:key="message"
+ android:restrictionType="string"
+ android:title="@string/title_message"/>
+
+ <restriction
+ android:defaultValue="@integer/default_number"
+ android:description="@string/description_number"
+ android:key="number"
+ android:restrictionType="integer"
+ android:title="@string/title_number"/>
+
+ <restriction
+ android:defaultValue="@string/default_rank"
+ android:description="@string/description_rank"
+ android:entries="@array/entries_rank"
+ android:entryValues="@array/entry_values_rank"
+ android:key="rank"
+ android:restrictionType="choice"
+ android:title="@string/title_rank"/>
+
+ <restriction
+ android:defaultValue="@array/default_approvals"
+ android:description="@string/description_approvals"
+ android:entries="@array/entries_approvals"
+ android:entryValues="@array/entry_values_approvals"
+ android:key="approvals"
+ android:restrictionType="multi-select"
+ android:title="@string/title_approvals"/>
+
+ <restriction
+ android:defaultValue="@string/default_secret_code"
+ android:description="@string/description_secret_code"
+ android:key="secret_code"
+ android:restrictionType="hidden"
+ android:title="@string/title_secret_code"/>
</restrictions>
diff --git a/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java b/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java
index 76f024f..7b8dba8 100644
--- a/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java
+++ b/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java
@@ -17,10 +17,12 @@
package com.example.android.apprestrictionschema;
import android.content.Context;
+import android.content.RestrictionEntry;
import android.content.RestrictionsManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,6 +32,8 @@
import com.example.android.common.logger.Log;
+import java.util.List;
+
/**
* Pressing the button on this fragment pops up a simple Toast message. The button is enabled or
* disabled according to the restrictions set by device/profile owner. You can use the
@@ -40,9 +44,21 @@
// Tag for the logger
private static final String TAG = "AppRestrictionSchemaFragment";
+ private static final String KEY_CAN_SAY_HELLO = "can_say_hello";
+ private static final String KEY_MESSAGE = "message";
+ private static final String KEY_NUMBER = "number";
+ private static final String KEY_RANK = "rank";
+ private static final String KEY_APPROVALS = "approvals";
+
+ // Message to show when the button is clicked (String restriction)
+ private String mMessage;
+
// UI Components
private TextView mTextSayHello;
private Button mButtonSayHello;
+ private TextView mTextNumber;
+ private TextView mTextRank;
+ private TextView mTextApprovals;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -54,48 +70,103 @@
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mTextSayHello = (TextView) view.findViewById(R.id.say_hello_explanation);
mButtonSayHello = (Button) view.findViewById(R.id.say_hello);
+ mTextNumber = (TextView) view.findViewById(R.id.your_number);
+ mTextRank = (TextView) view.findViewById(R.id.your_rank);
+ mTextApprovals = (TextView) view.findViewById(R.id.approvals_you_have);
mButtonSayHello.setOnClickListener(this);
}
@Override
public void onResume() {
super.onResume();
- // Update the UI according to the configured restrictions
- RestrictionsManager restrictionsManager =
- (RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
- Bundle restrictions = restrictionsManager.getApplicationRestrictions();
- updateUI(restrictions);
+ resolveRestrictions();
}
- private void updateUI(Bundle restrictions) {
- if (canSayHello(restrictions)) {
- mTextSayHello.setText(R.string.explanation_can_say_hello_true);
- mButtonSayHello.setEnabled(true);
- } else {
- mTextSayHello.setText(R.string.explanation_can_say_hello_false);
- mButtonSayHello.setEnabled(false);
+ private void resolveRestrictions() {
+ RestrictionsManager manager =
+ (RestrictionsManager) getActivity().getSystemService(Context.RESTRICTIONS_SERVICE);
+ Bundle restrictions = manager.getApplicationRestrictions();
+ List<RestrictionEntry> entries = manager.getManifestRestrictions(getActivity().getApplicationContext().getPackageName());
+ for (RestrictionEntry entry : entries) {
+ String key = entry.getKey();
+ Log.d(TAG, "key: " + key);
+ if (key.equals(KEY_CAN_SAY_HELLO)) {
+ updateCanSayHello(entry, restrictions);
+ } else if (key.equals(KEY_MESSAGE)) {
+ updateMessage(entry, restrictions);
+ } else if (key.equals(KEY_NUMBER)) {
+ updateNumber(entry, restrictions);
+ } else if (key.equals(KEY_RANK)) {
+ updateRank(entry, restrictions);
+ } else if (key.equals(KEY_APPROVALS)) {
+ updateApprovals(entry, restrictions);
+ }
}
}
- /**
- * Returns the current status of the restriction.
- *
- * @param restrictions The application restrictions
- * @return True if the app is allowed to say hello
- */
- private boolean canSayHello(Bundle restrictions) {
- final boolean defaultValue = false;
- boolean canSayHello = restrictions == null ? defaultValue :
- restrictions.getBoolean("can_say_hello", defaultValue);
- Log.d(TAG, "canSayHello: " + canSayHello);
- return canSayHello;
+ private void updateCanSayHello(RestrictionEntry entry, Bundle restrictions) {
+ boolean canSayHello;
+ if (restrictions == null || !restrictions.containsKey(KEY_CAN_SAY_HELLO)) {
+ canSayHello = entry.getSelectedState();
+ } else {
+ canSayHello = restrictions.getBoolean(KEY_CAN_SAY_HELLO);
+ }
+ mTextSayHello.setText(canSayHello ?
+ R.string.explanation_can_say_hello_true :
+ R.string.explanation_can_say_hello_false);
+ mButtonSayHello.setEnabled(canSayHello);
+ }
+
+ private void updateMessage(RestrictionEntry entry, Bundle restrictions) {
+ if (restrictions == null || !restrictions.containsKey(KEY_MESSAGE)) {
+ mMessage = entry.getSelectedString();
+ } else {
+ mMessage = restrictions.getString(KEY_MESSAGE);
+ }
+ }
+
+ private void updateNumber(RestrictionEntry entry, Bundle restrictions) {
+ int number;
+ if (restrictions == null || !restrictions.containsKey(KEY_NUMBER)) {
+ number = entry.getIntValue();
+ } else {
+ number = restrictions.getInt(KEY_NUMBER);
+ }
+ mTextNumber.setText(getString(R.string.your_number, number));
+ }
+
+ private void updateRank(RestrictionEntry entry, Bundle restrictions) {
+ String rank;
+ if (restrictions == null || !restrictions.containsKey(KEY_RANK)) {
+ rank = entry.getSelectedString();
+ } else {
+ rank = restrictions.getString(KEY_RANK);
+ }
+ mTextRank.setText(getString(R.string.your_rank, rank));
+ }
+
+ private void updateApprovals(RestrictionEntry entry, Bundle restrictions) {
+ String[] approvals;
+ if (restrictions == null || !restrictions.containsKey(KEY_APPROVALS)) {
+ approvals = entry.getAllSelectedStrings();
+ } else {
+ approvals = restrictions.getStringArray(KEY_APPROVALS);
+ }
+ String text;
+ if (approvals == null || approvals.length == 0) {
+ text = getString(R.string.none);
+ } else {
+ text = TextUtils.join(", ", approvals);
+ }
+ mTextApprovals.setText(getString(R.string.approvals_you_have, text));
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.say_hello: {
- Toast.makeText(getActivity(), R.string.message_hello, Toast.LENGTH_SHORT).show();
+ Toast.makeText(getActivity(), getString(R.string.message, mMessage),
+ Toast.LENGTH_SHORT).show();
break;
}
}
diff --git a/samples/browseable/AppRestrictions/res/layout/activity_main.xml b/samples/browseable/AppRestrictions/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/AppRestrictions/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/AppUsageStatistics/AndroidManifest.xml b/samples/browseable/AppUsageStatistics/AndroidManifest.xml
new file mode 100644
index 0000000..2726b4a
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 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.appusagestatistics"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/Theme.AppCompat.Light">
+
+ <activity android:name=".AppUsageStatisticsActivity"
+ 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/AppUsageStatistics/_index.jd b/samples/browseable/AppUsageStatistics/_index.jd
new file mode 100644
index 0000000..ee88115
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/_index.jd
@@ -0,0 +1,10 @@
+page.tags="AppUsageStatistics"
+sample.group=System
+@jd:body
+
+<p>
+
+ This sample explains how to use App usage statistics API, which was introduced
+ in Android 5.0.
+
+ </p>
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-hdpi/ic_default_app_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-hdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-hdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..3cf5e8b
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/AppUsageStatistics/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
copy to samples/browseable/AppUsageStatistics/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-mdpi/ic_default_app_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-mdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-mdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..51553c5
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-xhdpi/ic_default_app_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-xhdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-xhdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..5248000
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-xxhdpi/ic_default_app_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-xxhdpi/ic_default_app_launcher.png
new file mode 100644
index 0000000..4df1894
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-xxhdpi/ic_default_app_launcher.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AppUsageStatistics/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..7f16200
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AppUsageStatistics/res/layout/activity_app_usage_statistics.xml b/samples/browseable/AppUsageStatistics/res/layout/activity_app_usage_statistics.xml
new file mode 100644
index 0000000..2627c8b
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/layout/activity_app_usage_statistics.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.example.android.appusagestatistics.AppUsageStatisticsActivity"
+ tools:ignore="MergeRootFrame" />
diff --git a/samples/browseable/AppUsageStatistics/res/layout/fragment_app_usage_statistics.xml b/samples/browseable/AppUsageStatistics/res/layout/fragment_app_usage_statistics.xml
new file mode 100644
index 0000000..1d567b7
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/layout/fragment_app_usage_statistics.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="@dimen/margin_medium">
+
+ <Button android:id="@+id/button_open_usage_setting"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/open_app_usage_setting"
+ android:visibility="gone"
+ />
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/time_span"
+ />
+
+ <Spinner android:id="@+id/spinner_time_span"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recyclerview_app_usage"
+ android:layout_marginLeft="@dimen/margin_small"
+ android:layout_marginRight="@dimen/margin_small"
+ android:scrollbars="vertical"
+ android:drawSelectorOnTop="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/samples/browseable/AppUsageStatistics/res/layout/usage_row.xml b/samples/browseable/AppUsageStatistics/res/layout/usage_row.xml
new file mode 100644
index 0000000..c16f039
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/layout/usage_row.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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="@dimen/usage_row_height"
+ >
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
+ >
+ <ImageView android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_medium"
+ android:orientation="vertical"
+ >
+
+ <TextView
+ android:id="@+id/textview_package_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/PackageNameFont"
+ />
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+
+ <TextView
+ android:id="@+id/textview_last_time_used_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/last_time_used"
+ />
+
+ <TextView
+ android:id="@+id/textview_last_time_used"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <View android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="#bbbbbb"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/AppUsageStatistics/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/AppUsageStatistics/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/AppUsageStatistics/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
copy to samples/browseable/AppUsageStatistics/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/AppUsageStatistics/res/values-v11/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
copy to samples/browseable/AppUsageStatistics/res/values-v11/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-colors.xml b/samples/browseable/AppUsageStatistics/res/values-v21/base-colors.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-colors.xml
copy to samples/browseable/AppUsageStatistics/res/values-v21/base-colors.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml b/samples/browseable/AppUsageStatistics/res/values-v21/base-template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml
copy to samples/browseable/AppUsageStatistics/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml b/samples/browseable/AppUsageStatistics/res/values/base-strings.xml
similarity index 81%
copy from samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
copy to samples/browseable/AppUsageStatistics/res/values/base-strings.xml
index d653382..9a92da8 100644
--- a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
+++ b/samples/browseable/AppUsageStatistics/res/values/base-strings.xml
@@ -15,12 +15,13 @@
limitations under the License.
-->
<resources>
- <string name="app_name">WatchViewStub</string>
+ <string name="app_name">AppUsageStatistics</string>
<string name="intro_message">
<![CDATA[
- This sample demonstrates how to specify different layouts for round and rectangular screens.
+ This sample explains how to use App usage statistics API, which was introduced
+ in Android 5.0.
]]>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml b/samples/browseable/AppUsageStatistics/res/values/dimens.xml
similarity index 80%
rename from samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
rename to samples/browseable/AppUsageStatistics/res/values/dimens.xml
index 34c9cd1..d1741c3 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
+++ b/samples/browseable/AppUsageStatistics/res/values/dimens.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+
<resources>
-
-
-</resources>
+ <dimen name="usage_row_height">72dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml b/samples/browseable/AppUsageStatistics/res/values/navigation_items.xml
similarity index 73%
copy from samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
copy to samples/browseable/AppUsageStatistics/res/values/navigation_items.xml
index 34c9cd1..b36a4a5 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
+++ b/samples/browseable/AppUsageStatistics/res/values/navigation_items.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -15,6 +15,10 @@
limitations under the License.
-->
<resources>
-
-
-</resources>
+ <string-array name="action_list">
+ <item>Daily</item>
+ <item>Weekly</item>
+ <item>Monthly</item>
+ <item>Yearly</item>
+ </string-array>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/AppUsageStatistics/res/values/strings.xml b/samples/browseable/AppUsageStatistics/res/values/strings.xml
new file mode 100644
index 0000000..df769b5
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 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="open_app_usage_setting">Open Apps with usage access settings</string>
+ <string name="last_time_used">"Last time used: "</string>
+ <string name="time_span">"Time span: "</string>
+ <string name="explanation_access_to_appusage_is_not_enabled">Failed to retrieve app usage
+ statistics. You may need to enable access
+ for this app through Settings > Security > Apps with usage access
+ </string>
+</resources>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-template-styles.xml b/samples/browseable/AppUsageStatistics/res/values/styles.xml
similarity index 71%
rename from samples/browseable/EmbeddedApp/Application/res/values-v21/base-template-styles.xml
rename to samples/browseable/AppUsageStatistics/res/values/styles.xml
index 0b2948f..d400276 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-template-styles.xml
+++ b/samples/browseable/AppUsageStatistics/res/values/styles.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -15,9 +15,7 @@
limitations under the License.
-->
<resources>
-
- <!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Material.Light">
+ <style name="PackageNameFont" parent="@android:style/TextAppearance.Small">
+ <item name="android:textColor">#000000</item>
</style>
-
-</resources>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/AdapterTransition/res/values/template-dimens.xml b/samples/browseable/AppUsageStatistics/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-dimens.xml
copy to samples/browseable/AppUsageStatistics/res/values/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values/template-styles.xml b/samples/browseable/AppUsageStatistics/res/values/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-styles.xml
copy to samples/browseable/AppUsageStatistics/res/values/template-styles.xml
diff --git a/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/AppUsageStatisticsActivity.java b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/AppUsageStatisticsActivity.java
new file mode 100644
index 0000000..4def465
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/AppUsageStatisticsActivity.java
@@ -0,0 +1,37 @@
+/*
+* Copyright 2014 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.appusagestatistics;
+
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+
+/**
+ * Launcher Activity for the App Usage Statistics sample app.
+ */
+public class AppUsageStatisticsActivity extends ActionBarActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_app_usage_statistics);
+ if (savedInstanceState == null) {
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.container, AppUsageStatisticsFragment.newInstance())
+ .commit();
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/AppUsageStatisticsFragment.java b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/AppUsageStatisticsFragment.java
new file mode 100644
index 0000000..9f54d02
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/AppUsageStatisticsFragment.java
@@ -0,0 +1,233 @@
+/*
+* Copyright 2014 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.appusagestatistics;
+
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Fragment that demonstrates how to use App Usage Statistics API.
+ */
+public class AppUsageStatisticsFragment extends Fragment {
+
+ private static final String TAG = AppUsageStatisticsFragment.class.getSimpleName();
+
+ //VisibleForTesting for variables below
+ UsageStatsManager mUsageStatsManager;
+ UsageListAdapter mUsageListAdapter;
+ RecyclerView mRecyclerView;
+ RecyclerView.LayoutManager mLayoutManager;
+ Button mOpenUsageSettingButton;
+ Spinner mSpinner;
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @return A new instance of fragment {@link AppUsageStatisticsFragment}.
+ */
+ public static AppUsageStatisticsFragment newInstance() {
+ AppUsageStatisticsFragment fragment = new AppUsageStatisticsFragment();
+ return fragment;
+ }
+
+ public AppUsageStatisticsFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUsageStatsManager = (UsageStatsManager) getActivity()
+ .getSystemService("usagestats"); //Context.USAGE_STATS_SERVICE
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_app_usage_statistics, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View rootView, Bundle savedInstanceState) {
+ super.onViewCreated(rootView, savedInstanceState);
+
+ mLayoutManager = new LinearLayoutManager(getActivity());
+ mUsageListAdapter = new UsageListAdapter();
+ mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_app_usage);
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ mRecyclerView.scrollToPosition(0);
+ mRecyclerView.setAdapter(mUsageListAdapter);
+ mOpenUsageSettingButton = (Button) rootView.findViewById(R.id.button_open_usage_setting);
+ mSpinner = (Spinner) rootView.findViewById(R.id.spinner_time_span);
+ SpinnerAdapter spinnerAdapter = ArrayAdapter.createFromResource(getActivity(),
+ R.array.action_list, android.R.layout.simple_spinner_dropdown_item);
+ mSpinner.setAdapter(spinnerAdapter);
+ mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+
+ String[] strings = getResources().getStringArray(R.array.action_list);
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ StatsUsageInterval statsUsageInterval = StatsUsageInterval
+ .getValue(strings[position]);
+ if (statsUsageInterval != null) {
+ List<UsageStats> usageStatsList =
+ getUsageStatistics(statsUsageInterval.mInterval);
+ Collections.sort(usageStatsList, new LastTimeLaunchedComparatorDesc());
+ updateAppsList(usageStatsList);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
+ /**
+ * Returns the {@link #mRecyclerView} including the time span specified by the
+ * intervalType argument.
+ *
+ * @param intervalType The time interval by which the stats are aggregated.
+ * Corresponding to the value of {@link UsageStatsManager}.
+ * E.g. {@link UsageStatsManager#INTERVAL_DAILY}, {@link
+ * UsageStatsManager#INTERVAL_WEEKLY},
+ *
+ * @return A list of {@link android.app.usage.UsageStats}.
+ */
+ public List<UsageStats> getUsageStatistics(int intervalType) {
+ // Get the app statistics since one year ago from the current time.
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.YEAR, -1);
+
+ List<UsageStats> queryUsageStats = mUsageStatsManager
+ .queryUsageStats(intervalType, cal.getTimeInMillis(),
+ System.currentTimeMillis());
+
+ if (queryUsageStats.size() == 0) {
+ Log.i(TAG, "The user may not allow the access to apps usage. ");
+ Toast.makeText(getActivity(),
+ getString(R.string.explanation_access_to_appusage_is_not_enabled),
+ Toast.LENGTH_LONG).show();
+ mOpenUsageSettingButton.setVisibility(View.VISIBLE);
+ mOpenUsageSettingButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
+ }
+ });
+ }
+ return queryUsageStats;
+ }
+
+ /**
+ * Updates the {@link #mRecyclerView} with the list of {@link UsageStats} passed as an argument.
+ *
+ * @param usageStatsList A list of {@link UsageStats} from which update the
+ * {@link #mRecyclerView}.
+ */
+ //VisibleForTesting
+ void updateAppsList(List<UsageStats> usageStatsList) {
+ List<CustomUsageStats> customUsageStatsList = new ArrayList<>();
+ for (int i = 0; i < usageStatsList.size(); i++) {
+ CustomUsageStats customUsageStats = new CustomUsageStats();
+ customUsageStats.usageStats = usageStatsList.get(i);
+ try {
+ Drawable appIcon = getActivity().getPackageManager()
+ .getApplicationIcon(customUsageStats.usageStats.getPackageName());
+ customUsageStats.appIcon = appIcon;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, String.format("App Icon is not found for %s",
+ customUsageStats.usageStats.getPackageName()));
+ customUsageStats.appIcon = getActivity()
+ .getDrawable(R.drawable.ic_default_app_launcher);
+ }
+ customUsageStatsList.add(customUsageStats);
+ }
+ mUsageListAdapter.setCustomUsageStatsList(customUsageStatsList);
+ mUsageListAdapter.notifyDataSetChanged();
+ mRecyclerView.scrollToPosition(0);
+ }
+
+ /**
+ * The {@link Comparator} to sort a collection of {@link UsageStats} sorted by the timestamp
+ * last time the app was used in the descendant order.
+ */
+ private static class LastTimeLaunchedComparatorDesc implements Comparator<UsageStats> {
+
+ @Override
+ public int compare(UsageStats left, UsageStats right) {
+ return (int) (right.getLastTimeUsed() - left.getLastTimeUsed());
+ }
+ }
+
+ /**
+ * Enum represents the intervals for {@link android.app.usage.UsageStatsManager} so that
+ * values for intervals can be found by a String representation.
+ *
+ */
+ //VisibleForTesting
+ static enum StatsUsageInterval {
+ DAILY("Daily", UsageStatsManager.INTERVAL_DAILY),
+ WEEKLY("Weekly", UsageStatsManager.INTERVAL_WEEKLY),
+ MONTHLY("Monthly", UsageStatsManager.INTERVAL_MONTHLY),
+ YEARLY("Yearly", UsageStatsManager.INTERVAL_YEARLY);
+
+ private int mInterval;
+ private String mStringRepresentation;
+
+ StatsUsageInterval(String stringRepresentation, int interval) {
+ mStringRepresentation = stringRepresentation;
+ mInterval = interval;
+ }
+
+ static StatsUsageInterval getValue(String stringRepresentation) {
+ for (StatsUsageInterval statsUsageInterval : values()) {
+ if (statsUsageInterval.mStringRepresentation.equals(stringRepresentation)) {
+ return statsUsageInterval;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/CustomUsageStats.java b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/CustomUsageStats.java
new file mode 100644
index 0000000..b5b15c4
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/CustomUsageStats.java
@@ -0,0 +1,28 @@
+/*
+* Copyright 2014 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.appusagestatistics;
+
+import android.app.usage.UsageStats;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Entity class represents usage stats and app icon.
+ */
+public class CustomUsageStats {
+ public UsageStats usageStats;
+ public Drawable appIcon;
+}
diff --git a/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/UsageListAdapter.java b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/UsageListAdapter.java
new file mode 100644
index 0000000..ab1d037
--- /dev/null
+++ b/samples/browseable/AppUsageStatistics/src/com.example.android.appusagestatistics/UsageListAdapter.java
@@ -0,0 +1,95 @@
+/*
+* Copyright (C) 2014 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.appusagestatistics;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Provide views to RecyclerView with the directory entries.
+ */
+public class UsageListAdapter extends RecyclerView.Adapter<UsageListAdapter.ViewHolder> {
+
+ private List<CustomUsageStats> mCustomUsageStatsList = new ArrayList<>();
+ private DateFormat mDateFormat = new SimpleDateFormat();
+
+ /**
+ * Provide a reference to the type of views that you are using (custom ViewHolder)
+ */
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView mPackageName;
+ private final TextView mLastTimeUsed;
+ private final ImageView mAppIcon;
+
+ public ViewHolder(View v) {
+ super(v);
+ mPackageName = (TextView) v.findViewById(R.id.textview_package_name);
+ mLastTimeUsed = (TextView) v.findViewById(R.id.textview_last_time_used);
+ mAppIcon = (ImageView) v.findViewById(R.id.app_icon);
+ }
+
+ public TextView getLastTimeUsed() {
+ return mLastTimeUsed;
+ }
+
+ public TextView getPackageName() {
+ return mPackageName;
+ }
+
+ public ImageView getAppIcon() {
+ return mAppIcon;
+ }
+ }
+
+ public UsageListAdapter() {
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ View v = LayoutInflater.from(viewGroup.getContext())
+ .inflate(R.layout.usage_row, viewGroup, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, final int position) {
+ viewHolder.getPackageName().setText(
+ mCustomUsageStatsList.get(position).usageStats.getPackageName());
+ long lastTimeUsed = mCustomUsageStatsList.get(position).usageStats.getLastTimeUsed();
+ viewHolder.getLastTimeUsed().setText(mDateFormat.format(new Date(lastTimeUsed)));
+ viewHolder.getAppIcon().setImageDrawable(mCustomUsageStatsList.get(position).appIcon);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCustomUsageStatsList.size();
+ }
+
+ public void setCustomUsageStatsList(List<CustomUsageStats> customUsageStats) {
+ mCustomUsageStatsList = customUsageStats;
+ }
+}
\ No newline at end of file
diff --git a/samples/browseable/BasicAccessibility/res/layout/activity_main.xml b/samples/browseable/BasicAccessibility/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicAccessibility/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicContactables/res/layout/activity_main.xml b/samples/browseable/BasicContactables/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicContactables/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/ic_launcher.png
index b1efaf4..63dfb38 100644
--- a/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/ic_launcher.png
+++ b/samples/browseable/BasicImmersiveMode/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-mdpi/ic_launcher.png
index f5f9244..f7c06d0 100644
--- a/samples/browseable/BasicImmersiveMode/res/drawable-mdpi/ic_launcher.png
+++ b/samples/browseable/BasicImmersiveMode/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f..22d9247 100644
--- a/samples/browseable/BasicImmersiveMode/res/drawable-xhdpi/ic_launcher.png
+++ b/samples/browseable/BasicImmersiveMode/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicImmersiveMode/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicImmersiveMode/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1..4c4c037 100644
--- a/samples/browseable/BasicImmersiveMode/res/drawable-xxhdpi/ic_launcher.png
+++ b/samples/browseable/BasicImmersiveMode/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicManagedProfile/res/layout/activity_main.xml b/samples/browseable/BasicManagedProfile/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicManagedProfile/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicManagedProfile/res/layout/fragment_setup_profile.xml b/samples/browseable/BasicManagedProfile/res/layout/fragment_setup_profile.xml
index 2aaaa93..6b1e0d3 100644
--- a/samples/browseable/BasicManagedProfile/res/layout/fragment_setup_profile.xml
+++ b/samples/browseable/BasicManagedProfile/res/layout/fragment_setup_profile.xml
@@ -20,7 +20,7 @@
android:layout_height="match_parent"
tools:context="com.example.android.basicmanagedprofile.MainActivity.MainFragment">
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
diff --git a/samples/browseable/BasicMediaDecoder/res/layout/activity_main.xml b/samples/browseable/BasicMediaDecoder/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicMediaDecoder/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicMediaRouter/res/layout/activity_main.xml b/samples/browseable/BasicMediaRouter/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicMediaRouter/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicMultitouch/_index.jd b/samples/browseable/BasicMultitouch/_index.jd
index f09a7f4..187017a 100644
--- a/samples/browseable/BasicMultitouch/_index.jd
+++ b/samples/browseable/BasicMultitouch/_index.jd
@@ -4,9 +4,9 @@
<p>
-This samples demonstrates the use of MotionEvent properties to keep track of individual touches
+This sample demonstrates the use of MotionEvent properties to keep track of individual touches
across multiple touch events.
\n\nTouch the screen with multiple fingers to show that the pointer id
-(also represented by a colour) does not change as new touch events are received.</string>
+(also represented by a color) does not change as new touch events are received.
</p>
diff --git a/samples/browseable/BasicMultitouch/res/layout/activity_main.xml b/samples/browseable/BasicMultitouch/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicMultitouch/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicMultitouch/res/values/base-strings.xml b/samples/browseable/BasicMultitouch/res/values/base-strings.xml
index 9079d1e..a5ab6f5 100644
--- a/samples/browseable/BasicMultitouch/res/values/base-strings.xml
+++ b/samples/browseable/BasicMultitouch/res/values/base-strings.xml
@@ -20,10 +20,10 @@
<![CDATA[
-This samples demonstrates the use of MotionEvent properties to keep track of individual touches
+This sample demonstrates the use of MotionEvent properties to keep track of individual touches
across multiple touch events.
\n\nTouch the screen with multiple fingers to show that the pointer id
-(also represented by a colour) does not change as new touch events are received.</string>
+(also represented by a color) does not change as new touch events are received.
]]>
diff --git a/samples/browseable/BasicNetworking/res/layout/activity_main.xml b/samples/browseable/BasicNetworking/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicNetworking/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicNotifications/res/layout/activity_main.xml b/samples/browseable/BasicNotifications/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicNotifications/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicRenderScript/_index.jd b/samples/browseable/BasicRenderScript/_index.jd
index 3a4ffe0..3e6c62f 100644
--- a/samples/browseable/BasicRenderScript/_index.jd
+++ b/samples/browseable/BasicRenderScript/_index.jd
@@ -4,7 +4,8 @@
<p>
- BasicRenderScript sample demonstrates basic steps how to use renderScript.
- In the sample, it performs graphical filter operation on a image with renderScript.
+ This sample demonstrates using RenderScript to perform basic image manipulation. Specifically, it allows users
+ to dynamically adjust the saturation for an image using a slider. A custom RenderScript kernel performs the saturation
+ adjustment, running the computation on the device\'s GPU or other compute hardware as deemed appropriate by the system.
</p>
diff --git a/samples/browseable/BasicRenderScript/res/layout/activity_main.xml b/samples/browseable/BasicRenderScript/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicRenderScript/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicRenderScript/res/values/base-strings.xml b/samples/browseable/BasicRenderScript/res/values/base-strings.xml
index 36c7015..93a7391 100644
--- a/samples/browseable/BasicRenderScript/res/values/base-strings.xml
+++ b/samples/browseable/BasicRenderScript/res/values/base-strings.xml
@@ -20,8 +20,9 @@
<![CDATA[
- BasicRenderScript sample demonstrates basic steps how to use renderScript.
- In the sample, it performs graphical filter operation on a image with renderScript.
+ This sample demonstrates using RenderScript to perform basic image manipulation. Specifically, it allows users
+ to dynamically adjust the saturation for an image using a slider. A custom RenderScript kernel performs the saturation
+ adjustment, running the computation on the device\'s GPU or other compute hardware as deemed appropriate by the system.
]]>
diff --git a/samples/browseable/BasicSyncAdapter/res/layout/activity_main.xml b/samples/browseable/BasicSyncAdapter/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BasicSyncAdapter/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncAdapter.java b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncAdapter.java
index da67107..78d5865 100644
--- a/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncAdapter.java
+++ b/samples/browseable/BasicSyncAdapter/src/com.example.android.basicsyncadapter/SyncAdapter.java
@@ -250,9 +250,9 @@
// Update existing record
Log.i(TAG, "Scheduling update: " + existingUri);
batch.add(ContentProviderOperation.newUpdate(existingUri)
- .withValue(FeedContract.Entry.COLUMN_NAME_TITLE, title)
- .withValue(FeedContract.Entry.COLUMN_NAME_LINK, link)
- .withValue(FeedContract.Entry.COLUMN_NAME_PUBLISHED, published)
+ .withValue(FeedContract.Entry.COLUMN_NAME_TITLE, match.title)
+ .withValue(FeedContract.Entry.COLUMN_NAME_LINK, match.link)
+ .withValue(FeedContract.Entry.COLUMN_NAME_PUBLISHED, match.published)
.build());
syncResult.stats.numUpdates++;
} else {
diff --git a/samples/browseable/BatchStepSensor/res/layout/activity_main.xml b/samples/browseable/BatchStepSensor/res/layout/activity_main.xml
old mode 100755
new mode 100644
diff --git a/samples/browseable/BeamLargeFiles/AndroidManifest.xml b/samples/browseable/BeamLargeFiles/AndroidManifest.xml
new file mode 100644
index 0000000..b084253
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.beamlargefiles"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <!-- Large file beam requires API 16 or above. -->
+ <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
+
+ <!-- The NFC permission is required to use NfcAdapter. -->
+ <uses-permission android:name="android.permission.NFC" />
+ <!-- Inform app distribution channels that NFC is used, though not required. -->
+ <!-- (This step is optional, but recommended.) -->
+ <uses-feature android:name="android.hardware.nfc" android:required="false" />
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme">
+
+ <!-- Main activity -->
+ <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>
+
+ <!-- Content provider, used to provide images for transmission. -->
+ <provider
+ android:name="com.example.android.common.assetprovider.AssetProvider"
+ android:authorities="com.example.android.beamlargefiles.files"
+ android:exported="true"/>
+
+ </application>
+
+
+</manifest>
diff --git a/samples/browseable/BeamLargeFiles/_index.jd b/samples/browseable/BeamLargeFiles/_index.jd
new file mode 100644
index 0000000..d7b3e65
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/_index.jd
@@ -0,0 +1,16 @@
+page.tags="BeamLargeFiles"
+sample.group=Connectivity
+@jd:body
+
+<p>
+
+ This sample demonstrates how to transfer large files via Android Beam. After the initial
+ handshake over NFC, file transfer will take place over a secondary high-speed
+ communication channel such as Bluetooth or WiFi Direct.
+
+ \n\nThis feature requires Android 4.1 (Jelly Bean) or above. Unlike traditional Beam,
+ your application will not receive an Intent on the receiving device. Instead, the system
+ will save the file to disk and display a notification that the user can select to open
+ the file using a standard ACTION_VIEW Intent.
+
+ </p>
diff --git a/samples/browseable/BeamLargeFiles/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BeamLargeFiles/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..b1efaf4
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/BeamLargeFiles/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
copy to samples/browseable/BeamLargeFiles/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/BeamLargeFiles/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BeamLargeFiles/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f5f9244
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BeamLargeFiles/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BeamLargeFiles/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5d07b3f
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BeamLargeFiles/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BeamLargeFiles/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..6ef21e1
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BeamLargeFiles/res/layout-sw600dp-land/activity_main.xml b/samples/browseable/BeamLargeFiles/res/layout-sw600dp-land/activity_main.xml
new file mode 100644
index 0000000..8fed5f7
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/layout-sw600dp-land/activity_main.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.
+ -->
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:id="@+id/sample_main_layout">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView android:id="@+id/sample_output"
+ style="@style/Widget.SampleMessage"
+ android:background="@android:color/white"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/intro_message"
+ android:layout_margin="16dp" />
+ <fragment
+ android:name="com.example.android.common.logger.LogFragment"
+ android:id="@+id/log_fragment"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="16dp" />
+ </LinearLayout>
+</ScrollView>
diff --git a/samples/browseable/BeamLargeFiles/res/layout-sw600dp/activity_main.xml b/samples/browseable/BeamLargeFiles/res/layout-sw600dp/activity_main.xml
new file mode 100644
index 0000000..f811ef7
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/layout-sw600dp/activity_main.xml
@@ -0,0 +1,40 @@
+<!--
+ 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.
+ -->
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:id="@+id/sample_main_layout">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView android:id="@+id/sample_output"
+ style="@style/Widget.SampleMessage"
+ android:background="@android:color/white"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:layout_margin="16dp"/>
+ <fragment
+ android:name="com.example.android.common.logger.LogFragment"
+ android:id="@+id/log_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="16dp" />
+ </LinearLayout>
+</ScrollView>
diff --git a/samples/browseable/BeamLargeFiles/res/layout/activity_main.xml b/samples/browseable/BeamLargeFiles/res/layout/activity_main.xml
new file mode 100644
index 0000000..d170958
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/layout/activity_main.xml
@@ -0,0 +1,43 @@
+<!--
+ 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.
+ -->
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:id="@+id/sample_main_layout">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView android:id="@+id/sample_output"
+ style="@style/Widget.SampleMessage"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="16dp" />
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:background="@android:color/darker_gray"/>
+ <fragment
+ android:name="com.example.android.common.logger.LogFragment"
+ android:id="@+id/log_fragment"
+ android:layout_weight="1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </LinearLayout>
+</ScrollView>
diff --git a/samples/browseable/Flashlight/Application/res/values-v11/template-styles.xml b/samples/browseable/BeamLargeFiles/res/menu/main.xml
similarity index 76%
rename from samples/browseable/Flashlight/Application/res/values-v11/template-styles.xml
rename to samples/browseable/BeamLargeFiles/res/menu/main.xml
index 8c1ea66..8de3baa 100644
--- a/samples/browseable/Flashlight/Application/res/values-v11/template-styles.xml
+++ b/samples/browseable/BeamLargeFiles/res/menu/main.xml
@@ -1,5 +1,5 @@
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -14,9 +14,5 @@
limitations under the License.
-->
-<resources>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" />
- <!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-</resources>
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/BeamLargeFiles/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/BeamLargeFiles/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/BeamLargeFiles/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
copy to samples/browseable/BeamLargeFiles/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/BeamLargeFiles/res/values-v11/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
copy to samples/browseable/BeamLargeFiles/res/values-v11/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-colors.xml b/samples/browseable/BeamLargeFiles/res/values-v21/base-colors.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-colors.xml
copy to samples/browseable/BeamLargeFiles/res/values-v21/base-colors.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml b/samples/browseable/BeamLargeFiles/res/values-v21/base-template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml
copy to samples/browseable/BeamLargeFiles/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/BeamLargeFiles/res/values/base-strings.xml b/samples/browseable/BeamLargeFiles/res/values/base-strings.xml
new file mode 100644
index 0000000..6b2c69a
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/res/values/base-strings.xml
@@ -0,0 +1,35 @@
+<?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">BeamLargeFiles</string>
+ <string name="intro_message">
+ <![CDATA[
+
+
+ This sample demonstrates how to transfer large files via Android Beam. After the initial
+ handshake over NFC, file transfer will take place over a secondary high-speed
+ communication channel such as Bluetooth or WiFi Direct.
+
+ \n\nThis feature requires Android 4.1 (Jelly Bean) or above. Unlike traditional Beam,
+ your application will not receive an Intent on the receiving device. Instead, the system
+ will save the file to disk and display a notification that the user can select to open
+ the file using a standard ACTION_VIEW Intent.
+
+
+ ]]>
+ </string>
+</resources>
diff --git a/samples/browseable/AdapterTransition/res/values/template-dimens.xml b/samples/browseable/BeamLargeFiles/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-dimens.xml
copy to samples/browseable/BeamLargeFiles/res/values/template-dimens.xml
diff --git a/samples/browseable/EmbeddedApp/Application/res/values/template-styles.xml b/samples/browseable/BeamLargeFiles/res/values/template-styles.xml
similarity index 64%
rename from samples/browseable/EmbeddedApp/Application/res/values/template-styles.xml
rename to samples/browseable/BeamLargeFiles/res/values/template-styles.xml
index 6e7d593..cfffcbd 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values/template-styles.xml
+++ b/samples/browseable/BeamLargeFiles/res/values/template-styles.xml
@@ -18,18 +18,19 @@
<!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Light" />
+ <style name="Theme.Base" parent="android:Theme.Holo.Light" />
- <style name="Theme.Sample" parent="Theme.Base" />
-
- <style name="AppTheme" parent="Theme.Sample" />
+ <style name="AppTheme" parent="Theme.Base" />
<!-- Widget styling -->
<style name="Widget" />
<style name="Widget.SampleMessage">
+ <item name="android:padding">@dimen/margin_medium</item>
<item name="android:textAppearance">?android:textAppearanceMedium</item>
<item name="android:lineSpacingMultiplier">1.1</item>
+ <item name="android:layout_margin">16dp</item>
+ <item name="android:shadowDy">-6.5</item>
</style>
<style name="Widget.SampleMessageTile">
@@ -39,4 +40,15 @@
<item name="android:shadowRadius">2</item>
</style>
+
+ <style name="Widget.SampleOutput">
+ <item name="android:padding">@dimen/margin_medium</item>
+ <item name="android:textAppearance">?android:textAppearanceMedium</item>
+ <item name="android:lineSpacingMultiplier">1.1</item>
+ </style>
+
+ <style name="Log" parent="Widget.SampleOutput">
+ <item name="android:typeface">monospace</item>
+ </style>
+
</resources>
diff --git a/samples/browseable/BeamLargeFiles/src/com.example.android.beamlargefiles/BeamLargeFilesFragment.java b/samples/browseable/BeamLargeFiles/src/com.example.android.beamlargefiles/BeamLargeFilesFragment.java
new file mode 100644
index 0000000..6dd50fe
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/src/com.example.android.beamlargefiles/BeamLargeFilesFragment.java
@@ -0,0 +1,98 @@
+/*
+* 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.
+*/
+
+package com.example.android.beamlargefiles;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcEvent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+
+/**
+ * This class demonstrates how to use Beam to send files too large to transfer reliably via NFC.
+ *
+ * <p>While any type of data can be placed into a normal NDEF messages, NFC is not considered
+ * "high-speed" communication channel. Large images can easily take > 30 seconds to transfer.
+ * Because NFC requires devices to be in extremely close proximity, this is not ideal.
+ *
+ * <p>Instead, Android 4.2+ devices can use NFC to perform an initial handshake, before handing
+ * off to a faster communication channel, such as Bluetooth, for file transfer.
+ *
+ * <p>The tradeoff is that this application will not be invoked on the receiving device. Instead,
+ * the transfer will be handled by the OS. The user will be shown a notification when the transfer
+ * is complete. Selecting the notification will open the file in the default viewer for its MIME-
+ * type. (If it's important that your application be used to open the file, you'll need to register
+ * an intent-filter to watch for the appropriate MIME-type.)
+ */
+public class BeamLargeFilesFragment extends Fragment implements NfcAdapter.CreateBeamUrisCallback {
+
+ private static final String TAG = "BeamLargeFilesFragment";
+ /** Filename that is to be sent for this activity. Relative to /assets. */
+ private static final String FILENAME = "stargazer_droid.jpg";
+ /** Content provider URI. */
+ private static final String CONTENT_BASE_URI =
+ "content://com.example.android.beamlargefiles.files/";
+
+ /**
+ * Standard lifecycle event. Registers a callback for large-file transfer, by calling
+ * NfcAdapter.setBeamPushUrisCallback().
+ *
+ * Note: Like sending NDEF messages over standard Android Beam, there is also a non-callback
+ * API available. See: NfcAdapter.setBeamPushUris().
+ *
+ * @param savedInstanceState Saved instance state.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ Activity a = getActivity();
+
+ // Setup Beam to transfer a large file. Note the call to setBeamPushUrisCallback().
+ // BEGIN_INCLUDE(setBeamPushUrisCallback)
+ NfcAdapter nfc = NfcAdapter.getDefaultAdapter(a);
+ if (nfc != null) {
+ Log.w(TAG, "NFC available. Setting Beam Push URI callback");
+ nfc.setBeamPushUrisCallback(this, a);
+ } else {
+ Log.w(TAG, "NFC is not available");
+ }
+ // END_INCLUDE(setBeamPushUrisCallback)
+ }
+
+ /**
+ * Callback for Beam events (large file version). The return value here should be an array of
+ * content:// or file:// URIs to send.
+ *
+ * Note that the system must have read access to whatever URIs are provided here.
+ *
+ * @param nfcEvent NFC event which triggered callback
+ * @return URIs to be sent to remote device
+ */
+ // BEGIN_INCLUDE(createBeamUris)
+ @Override
+ public Uri[] createBeamUris(NfcEvent nfcEvent) {
+ Log.i(TAG, "Beam event in progress; createBeamUris() called.");
+ // Images are served using a content:// URI. See AssetProvider for implementation.
+ Uri photoUri = Uri.parse(CONTENT_BASE_URI + FILENAME);
+ Log.i(TAG, "Sending URI: " + photoUri);
+ return new Uri[] {photoUri};
+ }
+ // END_INCLUDE(createBeamUris)
+}
diff --git a/samples/browseable/BeamLargeFiles/src/com.example.android.beamlargefiles/MainActivity.java b/samples/browseable/BeamLargeFiles/src/com.example.android.beamlargefiles/MainActivity.java
new file mode 100644
index 0000000..50d504c
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/src/com.example.android.beamlargefiles/MainActivity.java
@@ -0,0 +1,82 @@
+/*
+* 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.beamlargefiles;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.text.Html;
+import android.widget.TextView;
+import android.view.Menu;
+
+import com.example.android.common.activities.SampleActivityBase;
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogFragment;
+import com.example.android.common.logger.LogWrapper;
+import com.example.android.common.logger.MessageOnlyLogFilter;
+
+/**
+ * A simple launcher activity containing a summary sample description
+ * and a few action bar buttons.
+ */
+public class MainActivity extends SampleActivityBase {
+
+ public static final String TAG = "MainActivity";
+
+ public static final String FRAGTAG = "BeamLargeFilesFragment";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ TextView sampleOutput = (TextView) findViewById(R.id.sample_output);
+ sampleOutput.setText(Html.fromHtml(getString(R.string.intro_message)));
+
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ BeamLargeFilesFragment fragment = new BeamLargeFilesFragment();
+ transaction.add(fragment, FRAGTAG);
+ transaction.commit();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ /** Create a chain of targets that will receive log data */
+ @Override
+ public void initializeLogging() {
+ // Wraps Android's native log framework.
+ LogWrapper logWrapper = new LogWrapper();
+ // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+ Log.setLogNode(logWrapper);
+
+ // Filter strips out everything except the message text.
+ MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
+ logWrapper.setNext(msgFilter);
+
+ // On screen logging via a fragment with a TextView.
+ LogFragment logFragment = (LogFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.log_fragment);
+ msgFilter.setNext(logFragment.getLogView());
+ logFragment.getLogView().setTextAppearance(this, R.style.Log);
+ logFragment.getLogView().setBackgroundColor(Color.WHITE);
+
+ Log.i(TAG, "Ready");
+ }
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
rename from samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
rename to samples/browseable/BeamLargeFiles/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/BeamLargeFiles/src/com.example.android.common/assetprovider/AssetProvider.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/assetprovider/AssetProvider.java
new file mode 100644
index 0000000..f6c0f61
--- /dev/null
+++ b/samples/browseable/BeamLargeFiles/src/com.example.android.common/assetprovider/AssetProvider.java
@@ -0,0 +1,140 @@
+/*
+* 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.
+*/
+
+package com.example.android.common.assetprovider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.database.Cursor;
+import android.net.Uri;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import static java.net.URLConnection.guessContentTypeFromName;
+
+/**
+ * Generic content provider, which makes any files available in this app's "assets" directory
+ * available publicly.
+ *
+ * <p>To use, add the following to your AndroidManifest.xml:
+ *
+ * <code><pre>
+ * <provider
+ * android:name=".AssetProvider"
+ * android:authorities="[YOUR CONTENT PROVIDER DOMAIN HERE]"
+ * android:exported="true"/>
+ * </pre></code>
+ */
+public class AssetProvider extends ContentProvider {
+ AssetManager mAssets;
+
+ @Override
+ public boolean onCreate() {
+ Context ctx = getContext();
+ if (ctx == null) {
+ // Context not available. Give up.
+ return false;
+ }
+ mAssets = ctx.getAssets();
+ return true;
+ }
+
+ @Override
+ public String getType(Uri uri){
+ // Returns the MIME type for the selected URI, in conformance with the ContentProvider
+ // interface. Looks up the file indicated by /res/assets/{uri.path}, and returns the MIME
+ // type for that file as guessed by the URLConnection class.
+
+ // Setup
+ String path = uri.getPath();
+
+ // Check if file exists
+ if (!fileExists(path)) {
+ return null;
+ }
+
+ // Determine MIME-type based on filename
+ return guessContentTypeFromName(uri.toString());
+ }
+
+
+ @Override
+ public AssetFileDescriptor openAssetFile (Uri uri, String mode)
+ throws FileNotFoundException, SecurityException {
+ // ContentProvider interface for opening a file descriptor by URI. This content provider
+ // maps all URIs to the contents of the APK's assets folder, so a file handle to
+ // /res/assets/{uri.path} will be returned.
+
+ // Security check. This content provider only supports read-only access. (Also, the contents
+ // of an APKs assets folder are immutable, so read-write access doesn't make sense here.)
+ if (!"r".equals(mode)) {
+ throw new SecurityException("Only read-only access is supported, mode must be [r]");
+ }
+
+ // Open asset from within APK and return file descriptor
+ String path = uri.getPath();
+ try {
+ return mAssets.openFd(path);
+ } catch (IOException e) {
+ throw new FileNotFoundException();
+ }
+ }
+
+ /**
+ * Check if file exists inside APK assets.
+ *
+ * @param path Fully qualified path to file.
+ * @return true if exists, false otherwise.
+ */
+ private boolean fileExists(String path) {
+ try {
+ // Check to see if file can be opened. If so, file exists.
+ mAssets.openFd(path).close();
+ return true;
+ } catch (IOException e) {
+ // Unable to open file descriptor for specified path; file doesn't exist.
+ return false;
+ }
+ }
+
+ // Required/unused ContentProvider methods below.
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ // Note: It might be worth implementing support for querying
+ // android.provider.OpenableColumns here in the future.
+ throw new RuntimeException("Operation not supported");
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues contentValues) {
+ throw new RuntimeException("Operation not supported");
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new RuntimeException("Operation not supported");
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new RuntimeException("Operation not supported");
+ }
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/Log.java
similarity index 100%
rename from samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
rename to samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
rename from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
rename to samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogNode.java
similarity index 100%
rename from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
rename to samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogView.java
similarity index 100%
rename from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
rename to samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
rename from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
rename to samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
rename from samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
rename to samples/browseable/BeamLargeFiles/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/BluetoothLeGatt/res/layout/activity_main.xml b/samples/browseable/BluetoothLeGatt/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BluetoothLeGatt/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_launcher.png
index b1efaf4..bb97e43 100644
--- a/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_launcher.png
+++ b/samples/browseable/BorderlessButtons/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_launcher.png
index f5f9244..4ee8ad3 100644
--- a/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_launcher.png
+++ b/samples/browseable/BorderlessButtons/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f..b3b78e9 100644
--- a/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_launcher.png
+++ b/samples/browseable/BorderlessButtons/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BorderlessButtons/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BorderlessButtons/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1..82a4165 100644
--- a/samples/browseable/BorderlessButtons/res/drawable-xxhdpi/ic_launcher.png
+++ b/samples/browseable/BorderlessButtons/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BorderlessButtons/res/layout/activity_main.xml b/samples/browseable/BorderlessButtons/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/BorderlessButtons/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/Camera2Basic/res/layout/activity_main.xml b/samples/browseable/Camera2Basic/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/Camera2Basic/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java b/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java
index 4b55358..2414ed6 100644
--- a/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java
+++ b/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java
@@ -43,6 +43,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Message;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
@@ -315,6 +316,32 @@
};
/**
+ * A {@link Handler} for showing {@link Toast}s.
+ */
+ private Handler mMessageHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Activity activity = getActivity();
+ if (activity != null) {
+ Toast.makeText(activity, (String) msg.obj, Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+
+ /**
+ * Shows a {@link Toast} on the UI thread.
+ *
+ * @param text The message to show
+ */
+ private void showToast(String text) {
+ // We show a Toast by sending request message to mMessageHandler. This makes sure that the
+ // Toast is shown on the UI thread.
+ Message message = Message.obtain();
+ message.obj = text;
+ mMessageHandler.sendMessage(message);
+ }
+
+ /**
* Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
* width and height are at least as large as the respective requested values, and whose aspect
* ratio matches with the specified value.
@@ -573,10 +600,7 @@
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
- Activity activity = getActivity();
- if (null != activity) {
- Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
- }
+ showToast("Failed");
}
}, null
);
@@ -689,7 +713,7 @@
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
- Toast.makeText(getActivity(), "Saved: " + mFile, Toast.LENGTH_SHORT).show();
+ showToast("Saved: " + mFile);
unlockFocus();
}
};
diff --git a/samples/browseable/Camera2Video/res/layout/activity_main.xml b/samples/browseable/Camera2Video/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/Camera2Video/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/CardView/res/layout/activity_main.xml b/samples/browseable/CardView/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/CardView/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/CustomChoiceList/res/layout/activity_main.xml b/samples/browseable/CustomChoiceList/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/CustomChoiceList/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/CustomNotifications/res/layout/activity_main.xml b/samples/browseable/CustomNotifications/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/CustomNotifications/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/DataLayer/Application/res/layout/activity_main.xml b/samples/browseable/DataLayer/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/DataLayer/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/DelayedConfirmation/Application/res/layout/activity_main.xml b/samples/browseable/DelayedConfirmation/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/DelayedConfirmation/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/DeviceOwner/AndroidManifest.xml b/samples/browseable/DeviceOwner/AndroidManifest.xml
new file mode 100644
index 0000000..fb61ac9
--- /dev/null
+++ b/samples/browseable/DeviceOwner/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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
+ package="com.example.android.deviceowner"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ 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>
+
+ <receiver
+ android:name=".DeviceOwnerReceiver"
+ android:description="@string/app_name"
+ android:label="@string/app_name"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data
+ android:name="android.app.device_admin"
+ android:resource="@xml/device_owner_receiver"/>
+ <intent-filter>
+ <action android:name="android.app.action.PROFILE_PROVISIONING_COMPLETE"/>
+ </intent-filter>
+ </receiver>
+
+ </application>
+
+
+</manifest>
diff --git a/samples/browseable/DeviceOwner/_index.jd b/samples/browseable/DeviceOwner/_index.jd
new file mode 100644
index 0000000..cd9b949
--- /dev/null
+++ b/samples/browseable/DeviceOwner/_index.jd
@@ -0,0 +1,12 @@
+page.tags="DeviceOwner"
+sample.group=Admin
+@jd:body
+
+<p>
+
+This app demonstrates how to use device owner features. Use the NfcProvisioning sample to set up
+this app as the device owner of your test device (This requires wiping out the device and resseting
+it as an unprovisioned device). As a device owner, this app can configure global settings, and
+enforce use of a specific launcher.
+
+ </p>
diff --git a/samples/browseable/DeviceOwner/res/drawable-hdpi/ic_launcher.png b/samples/browseable/DeviceOwner/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..282e540
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/DeviceOwner/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
copy to samples/browseable/DeviceOwner/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/DeviceOwner/res/drawable-mdpi/ic_launcher.png b/samples/browseable/DeviceOwner/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..4ecfdf6
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DeviceOwner/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/DeviceOwner/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..dfb6cab
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DeviceOwner/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/DeviceOwner/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..650302e
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DeviceOwner/res/layout/activity_main_real.xml b/samples/browseable/DeviceOwner/res/layout/activity_main_real.xml
new file mode 100644
index 0000000..384e9f9
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/layout/activity_main_real.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<FrameLayout
+ android:id="@+id/container"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.example.android.deviceowner.MainActivity"
+ tools:ignore="MergeRootFrame"/>
diff --git a/samples/browseable/DeviceOwner/res/layout/fragment_device_owner.xml b/samples/browseable/DeviceOwner/res/layout/fragment_device_owner.xml
new file mode 100644
index 0000000..aeae219
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/layout/fragment_device_owner.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.example.android.deviceowner.DeviceOwnerFragment">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_medium"
+ android:text="@string/label_global_settings"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <Switch
+ android:id="@+id/switch_auto_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_medium"
+ android:paddingEnd="@dimen/margin_medium"
+ android:paddingStart="@dimen/margin_medium"
+ android:text="@string/label_auto_time"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <Switch
+ android:id="@+id/switch_auto_time_zone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_medium"
+ android:paddingEnd="@dimen/margin_medium"
+ android:paddingStart="@dimen/margin_medium"
+ android:text="@string/label_auto_time_zone"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/margin_medium"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_medium"
+ android:text="@string/label_launcher"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <Spinner
+ android:id="@+id/available_launchers"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/margin_medium"
+ android:layout_marginTop="@dimen/margin_medium"/>
+
+ <Button
+ android:id="@+id/set_preferred_launcher"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/margin_medium"
+ android:text="@string/set_as_preferred"/>
+
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/samples/browseable/DeviceOwner/res/layout/fragment_instruction.xml b/samples/browseable/DeviceOwner/res/layout/fragment_instruction.xml
new file mode 100644
index 0000000..9d09e39
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/layout/fragment_instruction.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context="com.example.android.deviceowner.InstructionFragment">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_medium"
+ android:text="@string/set_up_instruction"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+</LinearLayout>
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/DeviceOwner/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/DeviceOwner/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/DeviceOwner/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
copy to samples/browseable/DeviceOwner/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/DeviceOwner/res/values-v11/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
copy to samples/browseable/DeviceOwner/res/values-v11/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-colors.xml b/samples/browseable/DeviceOwner/res/values-v21/base-colors.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-colors.xml
copy to samples/browseable/DeviceOwner/res/values-v21/base-colors.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml b/samples/browseable/DeviceOwner/res/values-v21/base-template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml
copy to samples/browseable/DeviceOwner/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml b/samples/browseable/DeviceOwner/res/values/base-strings.xml
similarity index 66%
copy from samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
copy to samples/browseable/DeviceOwner/res/values/base-strings.xml
index d653382..71d41f7 100644
--- a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
+++ b/samples/browseable/DeviceOwner/res/values/base-strings.xml
@@ -15,13 +15,16 @@
limitations under the License.
-->
<resources>
- <string name="app_name">WatchViewStub</string>
+ <string name="app_name">DeviceOwner</string>
<string name="intro_message">
<![CDATA[
-
- This sample demonstrates how to specify different layouts for round and rectangular screens.
-
+
+This app demonstrates how to use device owner features. Use the NfcProvisioning sample to set up
+this app as the device owner of your test device (This requires wiping out the device and resseting
+it as an unprovisioned device). As a device owner, this app can configure global settings, and
+enforce use of a specific launcher.
+
]]>
</string>
diff --git a/samples/browseable/DeviceOwner/res/values/strings.xml b/samples/browseable/DeviceOwner/res/values/strings.xml
new file mode 100644
index 0000000..af7d038
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/values/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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>
+
+ <string name="set_up_instruction">
+ The app is not set up as the device owner of this device. Use NfcProvisioning sample to set
+ up this app as the device owner of this device (This requires factory resetting the device).
+ </string>
+
+ <string name="profile_name">Sample Device Owner</string>
+
+ <string name="label_global_settings">Global settings</string>
+ <string name="label_auto_time">Automatic date & time</string>
+ <string name="label_auto_time_zone">Automatic time zone</string>
+
+ <string name="label_launcher">Home Launcher</string>
+ <string name="set_as_preferred">Set as preferred</string>
+ <string name="clear_preferred">Clear preferred</string>
+
+</resources>
diff --git a/samples/browseable/AdapterTransition/res/values/template-dimens.xml b/samples/browseable/DeviceOwner/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-dimens.xml
copy to samples/browseable/DeviceOwner/res/values/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values/template-styles.xml b/samples/browseable/DeviceOwner/res/values/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-styles.xml
copy to samples/browseable/DeviceOwner/res/values/template-styles.xml
diff --git a/samples/browseable/DeviceOwner/res/xml/device_owner_receiver.xml b/samples/browseable/DeviceOwner/res/xml/device_owner_receiver.xml
new file mode 100644
index 0000000..20109f0
--- /dev/null
+++ b/samples/browseable/DeviceOwner/res/xml/device_owner_receiver.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<device-admin>
+ <uses-policies>
+ <limit-password/>
+ <watch-login/>
+ <reset-password/>
+ <force-lock/>
+ <wipe-data/>
+ <expire-password/>
+ <encrypted-storage/>
+ <disable-camera/>
+ </uses-policies>
+</device-admin>
diff --git a/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/DeviceOwnerFragment.java b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/DeviceOwnerFragment.java
new file mode 100644
index 0000000..b5c6254
--- /dev/null
+++ b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/DeviceOwnerFragment.java
@@ -0,0 +1,334 @@
+/*
+ * 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.
+ */
+
+package com.example.android.deviceowner;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.SimpleAdapter;
+import android.widget.Spinner;
+import android.widget.Switch;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Demonstrates the usage of the most common device management APIs for the device owner case.
+ * In addition to various features available for profile owners, device owners can perform extra
+ * actions, such as configuring global settings and enforcing a preferred Activity for a specific
+ * IntentFilter.
+ */
+public class DeviceOwnerFragment extends Fragment {
+
+ // Keys for SharedPreferences
+ private static final String PREFS_DEVICE_OWNER = "DeviceOwnerFragment";
+ private static final String PREF_LAUNCHER = "launcher";
+
+ private DevicePolicyManager mDevicePolicyManager;
+
+ // View references
+ private Switch mSwitchAutoTime;
+ private Switch mSwitchAutoTimeZone;
+ private Spinner mAvailableLaunchers;
+ private Button mButtonLauncher;
+
+ // Adapter for the spinner to show list of available launchers
+ private LauncherAdapter mAdapter;
+
+ /**
+ * Handles events on the Switches.
+ */
+ private Switch.OnCheckedChangeListener mOnCheckedChangeListener
+ = new Switch.OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ switch (buttonView.getId()) {
+ case R.id.switch_auto_time:
+ setBooleanGlobalSetting(Settings.Global.AUTO_TIME, isChecked);
+ retrieveCurrentSettings(getActivity());
+ break;
+ case R.id.switch_auto_time_zone:
+ setBooleanGlobalSetting(Settings.Global.AUTO_TIME_ZONE, isChecked);
+ retrieveCurrentSettings(getActivity());
+ break;
+ }
+ }
+
+ };
+
+ /**
+ * Handles click events on the Button.
+ */
+ private View.OnClickListener mOnClickListener
+ = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.set_preferred_launcher:
+ if (loadPersistentPreferredLauncher(getActivity()) == null) {
+ setPreferredLauncher();
+ } else {
+ clearPreferredLauncher();
+ }
+ retrieveCurrentSettings(getActivity());
+ break;
+ }
+ }
+
+ };
+
+ /**
+ * @return A newly instantiated {@link DeviceOwnerFragment}.
+ */
+ public static DeviceOwnerFragment newInstance() {
+ return new DeviceOwnerFragment();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_device_owner, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ // Retain references
+ mSwitchAutoTime = (Switch) view.findViewById(R.id.switch_auto_time);
+ mSwitchAutoTimeZone = (Switch) view.findViewById(R.id.switch_auto_time_zone);
+ mAvailableLaunchers = (Spinner) view.findViewById(R.id.available_launchers);
+ mButtonLauncher = (Button) view.findViewById(R.id.set_preferred_launcher);
+ // Bind event handlers
+ mSwitchAutoTime.setOnCheckedChangeListener(mOnCheckedChangeListener);
+ mSwitchAutoTimeZone.setOnCheckedChangeListener(mOnCheckedChangeListener);
+ mButtonLauncher.setOnClickListener(mOnClickListener);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mDevicePolicyManager =
+ (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
+ }
+
+ @Override
+ public void onDetach() {
+ mDevicePolicyManager = null;
+ super.onDetach();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ Activity activity = getActivity();
+ if (activity != null) {
+ retrieveCurrentSettings(activity);
+ }
+ }
+
+ /**
+ * Retrieves the current global settings and changes the UI accordingly.
+ *
+ * @param activity The activity
+ */
+ private void retrieveCurrentSettings(Activity activity) {
+ // Global settings
+ setCheckedSafely(mSwitchAutoTime,
+ getBooleanGlobalSetting(activity.getContentResolver(), Settings.Global.AUTO_TIME));
+ setCheckedSafely(mSwitchAutoTimeZone,
+ getBooleanGlobalSetting(activity.getContentResolver(),
+ Settings.Global.AUTO_TIME_ZONE));
+
+ // Launcher
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ List<ResolveInfo> list = activity.getPackageManager()
+ .queryIntentActivities(intent, /* default flags */ 0);
+ mAdapter = new LauncherAdapter(activity, list);
+ mAvailableLaunchers.setAdapter(mAdapter);
+ String packageName = loadPersistentPreferredLauncher(activity);
+ if (packageName == null) { // No preferred launcher is set
+ mAvailableLaunchers.setEnabled(true);
+ mButtonLauncher.setText(R.string.set_as_preferred);
+ } else {
+ int position = -1;
+ for (int i = 0; i < list.size(); ++i) {
+ if (list.get(i).activityInfo.packageName.equals(packageName)) {
+ position = i;
+ break;
+ }
+ }
+ if (position != -1) {
+ mAvailableLaunchers.setSelection(position);
+ mAvailableLaunchers.setEnabled(false);
+ mButtonLauncher.setText(R.string.clear_preferred);
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current boolean value of the specified global setting.
+ *
+ * @param resolver The ContentResolver
+ * @param setting The setting to be retrieved
+ * @return The current boolean value
+ */
+ private static boolean getBooleanGlobalSetting(ContentResolver resolver, String setting) {
+ return 0 != Settings.Global.getInt(resolver, setting, 0);
+ }
+
+ /**
+ * Sets the boolean value of the specified global setting.
+ *
+ * @param setting The setting to be set
+ * @param value The value to be set
+ */
+ private void setBooleanGlobalSetting(String setting, boolean value) {
+ mDevicePolicyManager.setGlobalSetting(
+ // The ComponentName of the device owner
+ DeviceOwnerReceiver.getComponentName(getActivity()),
+ // The settings to be set
+ setting,
+ // The value we write here is a string representation for SQLite
+ value ? "1" : "0");
+ }
+
+ /**
+ * A utility method to set the checked state of the button without invoking its listener.
+ *
+ * @param button The button
+ * @param checked The value to be set
+ */
+ private void setCheckedSafely(CompoundButton button, boolean checked) {
+ button.setOnCheckedChangeListener(null);
+ button.setChecked(checked);
+ button.setOnCheckedChangeListener(mOnCheckedChangeListener);
+ }
+
+ /**
+ * Loads the package name from SharedPreferences.
+ *
+ * @param activity The activity
+ * @return The package name of the launcher currently set as preferred, or null if there is no
+ * preferred launcher.
+ */
+ private static String loadPersistentPreferredLauncher(Activity activity) {
+ return activity.getSharedPreferences(PREFS_DEVICE_OWNER, Context.MODE_PRIVATE)
+ .getString(PREF_LAUNCHER, null);
+ }
+
+ /**
+ * Saves the package name into SharedPreferences.
+ *
+ * @param activity The activity
+ * @param packageName The package name to be saved. Pass null to remove the preferred launcher.
+ */
+ private static void savePersistentPreferredLauncher(Activity activity, String packageName) {
+ SharedPreferences.Editor editor = activity.getSharedPreferences(PREFS_DEVICE_OWNER,
+ Context.MODE_PRIVATE).edit();
+ if (packageName == null) {
+ editor.remove(PREF_LAUNCHER);
+ } else {
+ editor.putString(PREF_LAUNCHER, packageName);
+ }
+ editor.apply();
+ }
+
+ /**
+ * Sets the selected launcher as preferred.
+ */
+ private void setPreferredLauncher() {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
+ filter.addCategory(Intent.CATEGORY_HOME);
+ filter.addCategory(Intent.CATEGORY_DEFAULT);
+ ComponentName componentName = mAdapter.getComponentName(
+ mAvailableLaunchers.getSelectedItemPosition());
+ mDevicePolicyManager.addPersistentPreferredActivity(
+ DeviceOwnerReceiver.getComponentName(activity), filter, componentName);
+ savePersistentPreferredLauncher(activity, componentName.getPackageName());
+ }
+
+ /**
+ * Clears the launcher currently set as preferred.
+ */
+ private void clearPreferredLauncher() {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ mDevicePolicyManager.clearPackagePersistentPreferredActivities(
+ DeviceOwnerReceiver.getComponentName(activity),
+ loadPersistentPreferredLauncher(activity));
+ savePersistentPreferredLauncher(activity, null);
+ }
+
+ /**
+ * Shows list of {@link ResolveInfo} in a {@link Spinner}.
+ */
+ private static class LauncherAdapter extends SimpleAdapter {
+
+ private static final String KEY_PACKAGE_NAME = "package_name";
+ private static final String KEY_ACTIVITY_NAME = "activity_name";
+
+ public LauncherAdapter(Context context, List<ResolveInfo> list) {
+ super(context, createData(list), android.R.layout.simple_list_item_1,
+ new String[]{KEY_PACKAGE_NAME},
+ new int[]{android.R.id.text1});
+ }
+
+ private static List<HashMap<String, String>> createData(List<ResolveInfo> list) {
+ List<HashMap<String, String>> data = new ArrayList<>();
+ for (ResolveInfo info : list) {
+ HashMap<String, String> map = new HashMap<>();
+ map.put(KEY_PACKAGE_NAME, info.activityInfo.packageName);
+ map.put(KEY_ACTIVITY_NAME, info.activityInfo.name);
+ data.add(map);
+ }
+ return data;
+ }
+
+ public ComponentName getComponentName(int position) {
+ @SuppressWarnings("unchecked")
+ HashMap<String, String> map = (HashMap<String, String>) getItem(position);
+ return new ComponentName(map.get(KEY_PACKAGE_NAME), map.get(KEY_ACTIVITY_NAME));
+ }
+
+ }
+
+}
diff --git a/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/DeviceOwnerReceiver.java b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/DeviceOwnerReceiver.java
new file mode 100644
index 0000000..f5969fc
--- /dev/null
+++ b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/DeviceOwnerReceiver.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package com.example.android.deviceowner;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Handles events related to device owner.
+ */
+public class DeviceOwnerReceiver extends DeviceAdminReceiver {
+
+ /**
+ * Called on the new profile when device owner provisioning has completed. Device owner
+ * provisioning is the process of setting up the device so that its main profile is managed by
+ * the mobile device management (MDM) application set up as the device owner.
+ */
+ @Override
+ public void onProfileProvisioningComplete(Context context, Intent intent) {
+ // Enable the profile
+ DevicePolicyManager manager =
+ (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ ComponentName componentName = getComponentName(context);
+ manager.setProfileName(componentName, context.getString(R.string.profile_name));
+ // Open the main screen
+ Intent launch = new Intent(context, MainActivity.class);
+ launch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(launch);
+ }
+
+ /**
+ * @return A newly instantiated {@link android.content.ComponentName} for this
+ * DeviceAdminReceiver.
+ */
+ public static ComponentName getComponentName(Context context) {
+ return new ComponentName(context.getApplicationContext(), DeviceOwnerReceiver.class);
+ }
+
+}
diff --git a/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/InstructionFragment.java b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/InstructionFragment.java
new file mode 100644
index 0000000..2a889c3
--- /dev/null
+++ b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/InstructionFragment.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package com.example.android.deviceowner;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Shows instruction about this device owner.
+ */
+public class InstructionFragment extends Fragment {
+
+ /**
+ * @return A newly instantiated {@link InstructionFragment}.
+ */
+ public static InstructionFragment newInstance() {
+ return new InstructionFragment();
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_instruction, container, false);
+ }
+
+}
diff --git a/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/MainActivity.java b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/MainActivity.java
new file mode 100644
index 0000000..5a331ab
--- /dev/null
+++ b/samples/browseable/DeviceOwner/src/com.example.android.deviceowner/MainActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package com.example.android.deviceowner;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+
+public class MainActivity extends Activity {
+
+ private static final String TAG = "MainActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main_real);
+ if (savedInstanceState == null) {
+ DevicePolicyManager manager =
+ (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (manager.isDeviceOwnerApp(getApplicationContext().getPackageName())) {
+ // This app is set up as the device owner. Show the main features.
+ Log.d(TAG, "The app is the device owner.");
+ showFragment(DeviceOwnerFragment.newInstance());
+ } else {
+ // This app is not set up as the device owner. Show instructions.
+ Log.d(TAG, "The app is not the device owner.");
+ showFragment(InstructionFragment.newInstance());
+ }
+ }
+ }
+
+ private void showFragment(Fragment fragment) {
+ getFragmentManager().beginTransaction()
+ .replace(R.id.container, fragment)
+ .commit();
+ }
+
+}
diff --git a/samples/browseable/DirectorySelection/AndroidManifest.xml b/samples/browseable/DirectorySelection/AndroidManifest.xml
new file mode 100644
index 0000000..46cddcf
--- /dev/null
+++ b/samples/browseable/DirectorySelection/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?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.directoryselection"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application android:allowBackup="true"
+ android:label="@string/app_name"
+ android:icon="@drawable/ic_launcher"
+ android:theme="@style/AppTheme">
+
+ <activity android:name=".DirectorySelectionActivity"
+ 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/DirectorySelection/_index.jd b/samples/browseable/DirectorySelection/_index.jd
new file mode 100644
index 0000000..4b394c2
--- /dev/null
+++ b/samples/browseable/DirectorySelection/_index.jd
@@ -0,0 +1,10 @@
+page.tags="DirectorySelection"
+sample.group=Content
+@jd:body
+
+<p>
+
+ This sample explains how to use Directory selection API, which was introduced
+ in Android 5.0.
+
+ </p>
diff --git a/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_description_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..dd7d073
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_folder_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..6c022d4
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_launcher.png b/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..49ee854
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/DirectorySelection/res/drawable-hdpi/tile.9.png
similarity index 100%
rename from samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
rename to samples/browseable/DirectorySelection/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_description_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..ac18b57
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_folder_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..e3dccd2
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_launcher.png b/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..282a00c
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_description_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..50f854e
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_folder_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..6fbc404
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..7293ad6
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_description_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_description_grey600_36dp.png
new file mode 100755
index 0000000..33df5d9
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_description_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_folder_grey600_36dp.png b/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_folder_grey600_36dp.png
new file mode 100755
index 0000000..ed2f08e
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_folder_grey600_36dp.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..7b618d4
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DirectorySelection/res/layout/activity_directory_selection.xml b/samples/browseable/DirectorySelection/res/layout/activity_directory_selection.xml
new file mode 100644
index 0000000..db65583
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/layout/activity_directory_selection.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.example.android.directoryselection.DirectorySelectionActivity"
+ tools:ignore="MergeRootFrame" />
\ No newline at end of file
diff --git a/samples/browseable/DirectorySelection/res/layout/directory_item.xml b/samples/browseable/DirectorySelection/res/layout/directory_item.xml
new file mode 100644
index 0000000..0763cff
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/layout/directory_item.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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="@dimen/directory_item_height"
+ >
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
+ >
+ <ImageView android:id="@+id/entry_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_folder_grey600_36dp"
+ />
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/margin_medium"
+ android:orientation="vertical"
+ >
+ <View android:id="@+id/divisor"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="#aaaaaa"/>
+
+ <TextView
+ android:id="@+id/textview_filename"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/DirectoryEntryNameFont"
+ />
+ <TextView
+ android:id="@+id/textview_mimetype"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+ </LinearLayout>
+</RelativeLayout>
diff --git a/samples/browseable/DirectorySelection/res/layout/fragment_directory_selection.xml b/samples/browseable/DirectorySelection/res/layout/fragment_directory_selection.xml
new file mode 100644
index 0000000..d63219c
--- /dev/null
+++ b/samples/browseable/DirectorySelection/res/layout/fragment_directory_selection.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="@dimen/margin_medium">
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+
+ <Button android:id="@+id/button_open_directory"
+ android:text="@string/open_directory"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <Button android:id="@+id/button_create_directory"
+ android:text="@string/create_directory"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ />
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginLeft="@dimen/margin_small"
+ android:layout_marginRight="@dimen/margin_small"
+ >
+
+ <TextView android:id="@+id/label_current_directory"
+ android:text="@string/selected_directory"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView android:id="@+id/textview_current_directory"
+ android:enabled="false"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/DirectoryEntryNameFont"
+ />
+
+ </LinearLayout>
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recyclerview_directory_entries"
+ android:layout_marginLeft="@dimen/margin_small"
+ android:layout_marginRight="@dimen/margin_small"
+ android:scrollbars="vertical"
+ android:drawSelectorOnTop="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</LinearLayout>
+
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml b/samples/browseable/DirectorySelection/res/menu/main.xml
similarity index 75%
rename from samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml
rename to samples/browseable/DirectorySelection/res/menu/main.xml
index 8c1ea66..e9b5e0b 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml
+++ b/samples/browseable/DirectorySelection/res/menu/main.xml
@@ -1,5 +1,5 @@
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -13,10 +13,4 @@
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>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/DirectorySelection/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
rename to samples/browseable/DirectorySelection/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/DirectorySelection/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
rename to samples/browseable/DirectorySelection/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/DirectorySelection/res/values-v11/template-styles.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
rename to samples/browseable/DirectorySelection/res/values-v11/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-colors.xml b/samples/browseable/DirectorySelection/res/values-v21/base-colors.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values-v21/base-colors.xml
rename to samples/browseable/DirectorySelection/res/values-v21/base-colors.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml b/samples/browseable/DirectorySelection/res/values-v21/base-template-styles.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml
rename to samples/browseable/DirectorySelection/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml b/samples/browseable/DirectorySelection/res/values/base-strings.xml
similarity index 81%
copy from samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
copy to samples/browseable/DirectorySelection/res/values/base-strings.xml
index d653382..782c64a 100644
--- a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
+++ b/samples/browseable/DirectorySelection/res/values/base-strings.xml
@@ -15,12 +15,13 @@
limitations under the License.
-->
<resources>
- <string name="app_name">WatchViewStub</string>
+ <string name="app_name">DirectorySelection</string>
<string name="intro_message">
<![CDATA[
- This sample demonstrates how to specify different layouts for round and rectangular screens.
+ This sample explains how to use Directory selection API, which was introduced
+ in Android 5.0.
]]>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml b/samples/browseable/DirectorySelection/res/values/dimens.xml
similarity index 79%
copy from samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
copy to samples/browseable/DirectorySelection/res/values/dimens.xml
index 34c9cd1..53d0182 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
+++ b/samples/browseable/DirectorySelection/res/values/dimens.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+
<resources>
-
-
-</resources>
+ <dimen name="directory_item_height">72dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml b/samples/browseable/DirectorySelection/res/values/strings.xml
similarity index 66%
copy from samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
copy to samples/browseable/DirectorySelection/res/values/strings.xml
index 34c9cd1..24d59dd 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
+++ b/samples/browseable/DirectorySelection/res/values/strings.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -15,6 +15,7 @@
limitations under the License.
-->
<resources>
-
-
+ <string name="open_directory">Open directory</string>
+ <string name="create_directory">Create Directory</string>
+ <string name="selected_directory">"Selected Directory : "</string>
</resources>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-template-styles.xml b/samples/browseable/DirectorySelection/res/values/styles.xml
similarity index 71%
copy from samples/browseable/EmbeddedApp/Application/res/values-v21/base-template-styles.xml
copy to samples/browseable/DirectorySelection/res/values/styles.xml
index 0b2948f..38441f3 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-template-styles.xml
+++ b/samples/browseable/DirectorySelection/res/values/styles.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -15,9 +15,7 @@
limitations under the License.
-->
<resources>
-
- <!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Material.Light">
+ <style name="DirectoryEntryNameFont" parent="@android:style/TextAppearance.Medium">
+ <item name="android:textColor">#000000</item>
</style>
-
-</resources>
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/AdapterTransition/res/values/template-dimens.xml b/samples/browseable/DirectorySelection/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values/template-dimens.xml
rename to samples/browseable/DirectorySelection/res/values/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values/template-styles.xml b/samples/browseable/DirectorySelection/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values/template-styles.xml
rename to samples/browseable/DirectorySelection/res/values/template-styles.xml
diff --git a/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectoryEntry.java b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectoryEntry.java
new file mode 100644
index 0000000..04c1c89
--- /dev/null
+++ b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectoryEntry.java
@@ -0,0 +1,25 @@
+/*
+* Copyright (C) 2014 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.directoryselection;
+
+/**
+ * Entity class that represents an directory entry.
+ */
+public class DirectoryEntry {
+ public String fileName;
+ public String mimeType;
+}
diff --git a/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectoryEntryAdapter.java b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectoryEntryAdapter.java
new file mode 100644
index 0000000..e92c71e
--- /dev/null
+++ b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectoryEntryAdapter.java
@@ -0,0 +1,100 @@
+/*
+* Copyright (C) 2014 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.directoryselection;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Provide views to RecyclerView with the directory entries.
+ */
+public class DirectoryEntryAdapter extends RecyclerView.Adapter<DirectoryEntryAdapter.ViewHolder> {
+
+ static final String DIRECTORY_MIME_TYPE = "vnd.android.document/directory";
+ private List<DirectoryEntry> mDirectoryEntries;
+
+ /**
+ * Provide a reference to the type of views that you are using (custom ViewHolder)
+ */
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView mFileName;
+ private final TextView mMimeType;
+ private final ImageView mImageView;
+
+ public ViewHolder(View v) {
+ super(v);
+ mFileName = (TextView) v.findViewById(R.id.textview_filename);
+ mMimeType = (TextView) v.findViewById(R.id.textview_mimetype);
+ mImageView = (ImageView) v.findViewById(R.id.entry_image);
+ }
+
+ public TextView getFileName() {
+ return mFileName;
+ }
+
+ public TextView getMimeType() {
+ return mMimeType;
+ }
+
+ public ImageView getImageView() {
+ return mImageView;
+ }
+ }
+
+ /**
+ * Initialize the directory entries of the Adapter.
+ *
+ * @param directoryEntries an array of {@link DirectoryEntry}.
+ */
+ public DirectoryEntryAdapter(List<DirectoryEntry> directoryEntries) {
+ mDirectoryEntries = directoryEntries;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ View v = LayoutInflater.from(viewGroup.getContext())
+ .inflate(R.layout.directory_item, viewGroup, false);
+ return new ViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, final int position) {
+ viewHolder.getFileName().setText(mDirectoryEntries.get(position).fileName);
+ viewHolder.getMimeType().setText(mDirectoryEntries.get(position).mimeType);
+
+ if (DIRECTORY_MIME_TYPE.equals(mDirectoryEntries.get(position).mimeType)) {
+ viewHolder.getImageView().setImageResource(R.drawable.ic_folder_grey600_36dp);
+ } else {
+ viewHolder.getImageView().setImageResource(R.drawable.ic_description_grey600_36dp);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDirectoryEntries.size();
+ }
+
+ public void setDirectoryEntries(List<DirectoryEntry> directoryEntries) {
+ mDirectoryEntries = directoryEntries;
+ }
+}
diff --git a/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectorySelectionActivity.java b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectorySelectionActivity.java
new file mode 100644
index 0000000..d27ba72
--- /dev/null
+++ b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectorySelectionActivity.java
@@ -0,0 +1,37 @@
+/*
+* Copyright 2014 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.directoryselection;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+
+/**
+ * Launcher Activity for the Directory Selection sample app.
+ */
+public class DirectorySelectionActivity extends FragmentActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_directory_selection);
+ if (savedInstanceState == null) {
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.container, DirectorySelectionFragment.newInstance())
+ .commit();
+ }
+ }
+}
diff --git a/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectorySelectionFragment.java b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectorySelectionFragment.java
new file mode 100644
index 0000000..4af55db
--- /dev/null
+++ b/samples/browseable/DirectorySelection/src/com.example.android.directoryselection/DirectorySelectionFragment.java
@@ -0,0 +1,231 @@
+/*
+* Copyright 2014 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.directoryselection;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment that demonstrates how to use Directory Selection API.
+ */
+public class DirectorySelectionFragment extends Fragment {
+
+ private static final String TAG = DirectorySelectionFragment.class.getSimpleName();
+
+ public static final int REQUEST_CODE_OPEN_DIRECTORY = 1;
+
+ Uri mCurrentDirectoryUri;
+ TextView mCurrentDirectoryTextView;
+ Button mCreateDirectoryButton;
+ RecyclerView mRecyclerView;
+ DirectoryEntryAdapter mAdapter;
+ RecyclerView.LayoutManager mLayoutManager;
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @return A new instance of fragment {@link DirectorySelectionFragment}.
+ */
+ public static DirectorySelectionFragment newInstance() {
+ DirectorySelectionFragment fragment = new DirectorySelectionFragment();
+ return fragment;
+ }
+
+ public DirectorySelectionFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_directory_selection, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View rootView, Bundle savedInstanceState) {
+ super.onViewCreated(rootView, savedInstanceState);
+
+ rootView.findViewById(R.id.button_open_directory)
+ .setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
+ startActivityForResult(intent, REQUEST_CODE_OPEN_DIRECTORY);
+ }
+ });
+
+ mCurrentDirectoryTextView = (TextView) rootView
+ .findViewById(R.id.textview_current_directory);
+ mCreateDirectoryButton = (Button) rootView.findViewById(R.id.button_create_directory);
+ mCreateDirectoryButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final EditText editView = new EditText(getActivity());
+ new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.create_directory)
+ .setView(editView)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ createDirectory(mCurrentDirectoryUri,
+ editView.getText().toString());
+ updateDirectoryEntries(mCurrentDirectoryUri);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ }
+ })
+ .show();
+ }
+ });
+ mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview_directory_entries);
+ mLayoutManager = new LinearLayoutManager(getActivity());
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ mRecyclerView.scrollToPosition(0);
+ mAdapter = new DirectoryEntryAdapter(new ArrayList<DirectoryEntry>());
+ mRecyclerView.setAdapter(mAdapter);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_CODE_OPEN_DIRECTORY && resultCode == Activity.RESULT_OK) {
+ Log.d(TAG, String.format("Open Directory result Uri : %s", data.getData()));
+ updateDirectoryEntries(data.getData());
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+
+ /**
+ * Updates the current directory of the uri passed as an argument and its children directories.
+ * And updates the {@link #mRecyclerView} depending on the contents of the children.
+ *
+ * @param uri The uri of the current directory.
+ */
+ //VisibileForTesting
+ void updateDirectoryEntries(Uri uri) {
+ ContentResolver contentResolver = getActivity().getContentResolver();
+ Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
+ DocumentsContract.getTreeDocumentId(uri));
+ Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
+ DocumentsContract.getTreeDocumentId(uri));
+
+ Cursor docCursor = contentResolver.query(docUri, new String[]{
+ Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);
+ try {
+ while (docCursor.moveToNext()) {
+ Log.d(TAG, "found doc =" + docCursor.getString(0) + ", mime=" + docCursor
+ .getString(1));
+ mCurrentDirectoryUri = uri;
+ mCurrentDirectoryTextView.setText(docCursor.getString(0));
+ mCreateDirectoryButton.setEnabled(true);
+ }
+ } finally {
+ closeQuietly(docCursor);
+ }
+
+ Cursor childCursor = contentResolver.query(childrenUri, new String[]{
+ Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE}, null, null, null);
+ try {
+ List<DirectoryEntry> directoryEntries = new ArrayList<>();
+ while (childCursor.moveToNext()) {
+ Log.d(TAG, "found child=" + childCursor.getString(0) + ", mime=" + childCursor
+ .getString(1));
+ DirectoryEntry entry = new DirectoryEntry();
+ entry.fileName = childCursor.getString(0);
+ entry.mimeType = childCursor.getString(1);
+ directoryEntries.add(entry);
+ }
+ mAdapter.setDirectoryEntries(directoryEntries);
+ mAdapter.notifyDataSetChanged();
+ } finally {
+ closeQuietly(childCursor);
+ }
+ }
+
+ /**
+ * Creates a directory under the directory represented as the uri in the argument.
+ *
+ * @param uri The uri of the directory under which a new directory is created.
+ * @param directoryName The directory name of a new directory.
+ */
+ //VisibileForTesting
+ void createDirectory(Uri uri, String directoryName) {
+ ContentResolver contentResolver = getActivity().getContentResolver();
+ Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
+ DocumentsContract.getTreeDocumentId(uri));
+ Uri directoryUri = DocumentsContract
+ .createDocument(contentResolver, docUri, Document.MIME_TYPE_DIR, directoryName);
+ if (directoryUri != null) {
+ Log.i(TAG, String.format(
+ "Created directory : %s, Document Uri : %s, Created directory Uri : %s",
+ directoryName, docUri, directoryUri));
+ Toast.makeText(getActivity(), String.format("Created a directory [%s]",
+ directoryName), Toast.LENGTH_SHORT).show();
+ } else {
+ Log.w(TAG, String.format("Failed to create a directory : %s, Uri %s", directoryName,
+ docUri));
+ Toast.makeText(getActivity(), String.format("Failed to created a directory [%s] : ",
+ directoryName), Toast.LENGTH_SHORT).show();
+ }
+
+ }
+
+ public void closeQuietly(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+}
+
diff --git a/samples/browseable/DisplayingBitmaps/res/layout/activity_main.xml b/samples/browseable/DisplayingBitmaps/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/DisplayingBitmaps/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/DocumentCentricApps/res/drawable-hdpi/ic_launcher.png b/samples/browseable/DocumentCentricApps/res/drawable-hdpi/ic_launcher.png
index b1efaf4..15e44c5 100644
--- a/samples/browseable/DocumentCentricApps/res/drawable-hdpi/ic_launcher.png
+++ b/samples/browseable/DocumentCentricApps/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DocumentCentricApps/res/drawable-mdpi/ic_launcher.png b/samples/browseable/DocumentCentricApps/res/drawable-mdpi/ic_launcher.png
index f5f9244..ba0bd22 100644
--- a/samples/browseable/DocumentCentricApps/res/drawable-mdpi/ic_launcher.png
+++ b/samples/browseable/DocumentCentricApps/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DocumentCentricApps/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/DocumentCentricApps/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f..42d49b8 100644
--- a/samples/browseable/DocumentCentricApps/res/drawable-xhdpi/ic_launcher.png
+++ b/samples/browseable/DocumentCentricApps/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DocumentCentricApps/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/DocumentCentricApps/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1..3624eef 100644
--- a/samples/browseable/DocumentCentricApps/res/drawable-xxhdpi/ic_launcher.png
+++ b/samples/browseable/DocumentCentricApps/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/DocumentCentricApps/res/layout/activity_main.xml b/samples/browseable/DocumentCentricApps/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/DocumentCentricApps/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/DocumentCentricRelinquishIdentity/res/layout/activity_main.xml b/samples/browseable/DocumentCentricRelinquishIdentity/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/DocumentCentricRelinquishIdentity/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/ElizaChat/Application/AndroidManifest.xml b/samples/browseable/ElizaChat/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/AndroidManifest.xml
rename to samples/browseable/ElizaChat/AndroidManifest.xml
diff --git a/samples/browseable/ElizaChat/Application/res/layout/activity_main.xml b/samples/browseable/ElizaChat/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/ElizaChat/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/ElizaChat/Application/res/drawable-hdpi/ic_full_reply.png b/samples/browseable/ElizaChat/res/drawable-hdpi/ic_full_reply.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/drawable-hdpi/ic_full_reply.png
rename to samples/browseable/ElizaChat/res/drawable-hdpi/ic_full_reply.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/ElizaChat/res/drawable-hdpi/tile.9.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/drawable-hdpi/tile.9.png
rename to samples/browseable/ElizaChat/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/drawable-mdpi/ic_full_reply.png b/samples/browseable/ElizaChat/res/drawable-mdpi/ic_full_reply.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/drawable-mdpi/ic_full_reply.png
rename to samples/browseable/ElizaChat/res/drawable-mdpi/ic_full_reply.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/drawable-nodpi/bg_eliza.png b/samples/browseable/ElizaChat/res/drawable-nodpi/bg_eliza.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/drawable-nodpi/bg_eliza.png
rename to samples/browseable/ElizaChat/res/drawable-nodpi/bg_eliza.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/drawable-xhdpi/ic_full_reply.png b/samples/browseable/ElizaChat/res/drawable-xhdpi/ic_full_reply.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/drawable-xhdpi/ic_full_reply.png
rename to samples/browseable/ElizaChat/res/drawable-xhdpi/ic_full_reply.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/layout/main.xml b/samples/browseable/ElizaChat/res/layout/main.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/layout/main.xml
rename to samples/browseable/ElizaChat/res/layout/main.xml
diff --git a/samples/browseable/ElizaChat/Application/res/menu/main.xml b/samples/browseable/ElizaChat/res/menu/main.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/menu/main.xml
rename to samples/browseable/ElizaChat/res/menu/main.xml
diff --git a/samples/browseable/ElizaChat/Application/res/mipmap-hdpi/ic_app_eliza.png b/samples/browseable/ElizaChat/res/mipmap-hdpi/ic_app_eliza.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/mipmap-hdpi/ic_app_eliza.png
rename to samples/browseable/ElizaChat/res/mipmap-hdpi/ic_app_eliza.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/mipmap-mdpi/ic_app_eliza.png b/samples/browseable/ElizaChat/res/mipmap-mdpi/ic_app_eliza.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/mipmap-mdpi/ic_app_eliza.png
rename to samples/browseable/ElizaChat/res/mipmap-mdpi/ic_app_eliza.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/mipmap-xhdpi/ic_app_eliza.png b/samples/browseable/ElizaChat/res/mipmap-xhdpi/ic_app_eliza.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/mipmap-xhdpi/ic_app_eliza.png
rename to samples/browseable/ElizaChat/res/mipmap-xhdpi/ic_app_eliza.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/mipmap-xxhdpi/ic_app_eliza.png b/samples/browseable/ElizaChat/res/mipmap-xxhdpi/ic_app_eliza.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/mipmap-xxhdpi/ic_app_eliza.png
rename to samples/browseable/ElizaChat/res/mipmap-xxhdpi/ic_app_eliza.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/mipmap-xxxhdpi/ic_app_eliza.png b/samples/browseable/ElizaChat/res/mipmap-xxxhdpi/ic_app_eliza.png
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/mipmap-xxxhdpi/ic_app_eliza.png
rename to samples/browseable/ElizaChat/res/mipmap-xxxhdpi/ic_app_eliza.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/ElizaChat/res/values-sw600dp/template-dimens.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values-sw600dp/template-dimens.xml
rename to samples/browseable/ElizaChat/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/ElizaChat/res/values-sw600dp/template-styles.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values-sw600dp/template-styles.xml
rename to samples/browseable/ElizaChat/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values-v11/template-styles.xml b/samples/browseable/ElizaChat/res/values-v11/template-styles.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values-v11/template-styles.xml
rename to samples/browseable/ElizaChat/res/values-v11/template-styles.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values-v21/base-colors.xml b/samples/browseable/ElizaChat/res/values-v21/base-colors.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values-v21/base-colors.xml
rename to samples/browseable/ElizaChat/res/values-v21/base-colors.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values-v21/base-template-styles.xml b/samples/browseable/ElizaChat/res/values-v21/base-template-styles.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values-v21/base-template-styles.xml
rename to samples/browseable/ElizaChat/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values/base-strings.xml b/samples/browseable/ElizaChat/res/values/base-strings.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values/base-strings.xml
rename to samples/browseable/ElizaChat/res/values/base-strings.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values/dimens.xml b/samples/browseable/ElizaChat/res/values/dimens.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values/dimens.xml
rename to samples/browseable/ElizaChat/res/values/dimens.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values/strings.xml b/samples/browseable/ElizaChat/res/values/strings.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values/strings.xml
rename to samples/browseable/ElizaChat/res/values/strings.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values/template-dimens.xml b/samples/browseable/ElizaChat/res/values/template-dimens.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values/template-dimens.xml
rename to samples/browseable/ElizaChat/res/values/template-dimens.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values/template-styles.xml b/samples/browseable/ElizaChat/res/values/template-styles.xml
similarity index 100%
rename from samples/browseable/ElizaChat/Application/res/values/template-styles.xml
rename to samples/browseable/ElizaChat/res/values/template-styles.xml
diff --git a/samples/browseable/ElizaChat/Application/src/com.example.android.wearable.elizachat/ElizaResponder.java b/samples/browseable/ElizaChat/src/com.example.android.wearable.elizachat/ElizaResponder.java
similarity index 100%
rename from samples/browseable/ElizaChat/Application/src/com.example.android.wearable.elizachat/ElizaResponder.java
rename to samples/browseable/ElizaChat/src/com.example.android.wearable.elizachat/ElizaResponder.java
diff --git a/samples/browseable/ElizaChat/Application/src/com.example.android.wearable.elizachat/MainActivity.java b/samples/browseable/ElizaChat/src/com.example.android.wearable.elizachat/MainActivity.java
similarity index 100%
rename from samples/browseable/ElizaChat/Application/src/com.example.android.wearable.elizachat/MainActivity.java
rename to samples/browseable/ElizaChat/src/com.example.android.wearable.elizachat/MainActivity.java
diff --git a/samples/browseable/ElizaChat/Application/src/com.example.android.wearable.elizachat/ResponderService.java b/samples/browseable/ElizaChat/src/com.example.android.wearable.elizachat/ResponderService.java
similarity index 100%
rename from samples/browseable/ElizaChat/Application/src/com.example.android.wearable.elizachat/ResponderService.java
rename to samples/browseable/ElizaChat/src/com.example.android.wearable.elizachat/ResponderService.java
diff --git a/samples/browseable/EmbeddedApp/Application/AndroidManifest.xml b/samples/browseable/EmbeddedApp/Application/AndroidManifest.xml
deleted file mode 100644
index ee47ffe..0000000
--- a/samples/browseable/EmbeddedApp/Application/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.embeddedapp" >
- <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@android:style/Theme.DeviceDefault.Light" >
- <activity
- android:name=".PhoneActivity"
- 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/EmbeddedApp/Application/res/drawable-hdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Application/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/EmbeddedApp/Application/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Application/res/drawable-mdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Application/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Application/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Application/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Application/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Application/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Application/res/layout/activity_main.xml b/samples/browseable/EmbeddedApp/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/EmbeddedApp/Application/res/layout/activity_phone.xml b/samples/browseable/EmbeddedApp/Application/res/layout/activity_phone.xml
deleted file mode 100644
index 54ca57e..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/layout/activity_phone.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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="match_parent"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- android:paddingBottom="@dimen/activity_vertical_margin"
- tools:context=".PhoneActivity">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:text="@string/welcome_message"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true" />
-</RelativeLayout>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/EmbeddedApp/Application/res/values/base-strings.xml b/samples/browseable/EmbeddedApp/Application/res/values/base-strings.xml
deleted file mode 100644
index 46059ed..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/values/base-strings.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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">EmbeddedApp</string>
- <string name="intro_message">
- <![CDATA[
-
-
- This simple app demonstrates how to embed a wearable app into a phone app.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values/dimens.xml b/samples/browseable/EmbeddedApp/Application/res/values/dimens.xml
deleted file mode 100644
index a1e9cfe..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/values/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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="activity_horizontal_margin">16dp</dimen>
- <dimen name="activity_vertical_margin">16dp</dimen>
-</resources>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values/strings.xml b/samples/browseable/EmbeddedApp/Application/res/values/strings.xml
deleted file mode 100644
index 05b6f12..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/values/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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="welcome_message">
- By installing the release build of this application, the corresponding wearable app should
- automatically be installed on any connected wearable devices.
- </string>
-
-</resources>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values/template-dimens.xml b/samples/browseable/EmbeddedApp/Application/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/EmbeddedApp/Application/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/EmbeddedApp/Application/src/com.example.android.wearable.embeddedapp/PhoneActivity.java b/samples/browseable/EmbeddedApp/Application/src/com.example.android.wearable.embeddedapp/PhoneActivity.java
deleted file mode 100644
index 5a2f5ca..0000000
--- a/samples/browseable/EmbeddedApp/Application/src/com.example.android.wearable.embeddedapp/PhoneActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 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.wearable.embeddedapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class PhoneActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_phone);
- }
-}
diff --git a/samples/browseable/EmbeddedApp/Wearable/AndroidManifest.xml b/samples/browseable/EmbeddedApp/Wearable/AndroidManifest.xml
deleted file mode 100644
index 4863d66..0000000
--- a/samples/browseable/EmbeddedApp/Wearable/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.embeddedapp" >
-
- <uses-sdk android:minSdkVersion="20"
- android:targetSdkVersion="21" />
-
- <uses-feature android:name="android.hardware.type.watch" />
-
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@android:style/Theme.DeviceDefault.Light" >
- <activity
- android:name=".WearableActivity"
- 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/EmbeddedApp/Wearable/res/drawable-hdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Wearable/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/EmbeddedApp/Wearable/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Wearable/res/drawable-mdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Wearable/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/EmbeddedApp/Wearable/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Wearable/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Wearable/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/EmbeddedApp/Wearable/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Wearable/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/EmbeddedApp/Wearable/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/EmbeddedApp/Wearable/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Wearable/res/layout/activity_wearable.xml b/samples/browseable/EmbeddedApp/Wearable/res/layout/activity_wearable.xml
deleted file mode 100644
index 1f6a78a..0000000
--- a/samples/browseable/EmbeddedApp/Wearable/res/layout/activity_wearable.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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="match_parent"
- tools:context=".WearableActivity">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/welcome_message"
- android:layout_centerVertical="true"
- android:layout_centerHorizontal="true" />
-</RelativeLayout>
diff --git a/samples/browseable/EmbeddedApp/Wearable/res/values/strings.xml b/samples/browseable/EmbeddedApp/Wearable/res/values/strings.xml
deleted file mode 100644
index 1823c57..0000000
--- a/samples/browseable/EmbeddedApp/Wearable/res/values/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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">EmbeddedApp Sample</string>
- <string name="welcome_message">
- This wearable app should be automatically installed on wearable devices whenever the
- release build of the corresponding phone app is installed.
- </string>
-
-</resources>
diff --git a/samples/browseable/EmbeddedApp/_index.jd b/samples/browseable/EmbeddedApp/_index.jd
deleted file mode 100644
index b8d047f..0000000
--- a/samples/browseable/EmbeddedApp/_index.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.tags="EmbeddedApp"
-sample.group=Wearable
-@jd:body
-
-<p>
-
- This simple app demonstrates how to embed a wearable app into a phone app.
-
- </p>
diff --git a/samples/browseable/FindMyPhone/Application/res/layout/activity_main.xml b/samples/browseable/FindMyPhone/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/FindMyPhone/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/Flashlight/Wearable/AndroidManifest.xml b/samples/browseable/Flashlight/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/Flashlight/Wearable/AndroidManifest.xml
rename to samples/browseable/Flashlight/AndroidManifest.xml
diff --git a/samples/browseable/Flashlight/Application/AndroidManifest.xml b/samples/browseable/Flashlight/Application/AndroidManifest.xml
deleted file mode 100644
index 925d11b..0000000
--- a/samples/browseable/Flashlight/Application/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2014 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.flashlight">
-
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="21" />
-
- <application android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
- </application>
-
-</manifest>
-
diff --git a/samples/browseable/Flashlight/Application/res/drawable-hdpi/ic_launcher.png b/samples/browseable/Flashlight/Application/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/Flashlight/Application/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/Flashlight/Application/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/Flashlight/Application/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Application/res/drawable-mdpi/ic_launcher.png b/samples/browseable/Flashlight/Application/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/Flashlight/Application/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Application/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/Flashlight/Application/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/Flashlight/Application/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Application/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/Flashlight/Application/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/Flashlight/Application/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Application/res/layout/activity_main.xml b/samples/browseable/Flashlight/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/Flashlight/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/Flashlight/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/Flashlight/Application/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2..0000000
--- a/samples/browseable/Flashlight/Application/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- 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/Flashlight/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/Flashlight/Application/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/Flashlight/Application/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/Flashlight/Application/res/values-v21/base-colors.xml b/samples/browseable/Flashlight/Application/res/values-v21/base-colors.xml
deleted file mode 100644
index 34c9cd1..0000000
--- a/samples/browseable/Flashlight/Application/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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>
-
-
-</resources>
diff --git a/samples/browseable/Flashlight/Application/res/values-v21/base-template-styles.xml b/samples/browseable/Flashlight/Application/res/values-v21/base-template-styles.xml
deleted file mode 100644
index 0b2948f..0000000
--- a/samples/browseable/Flashlight/Application/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
- </style>
-
-</resources>
diff --git a/samples/browseable/Flashlight/Application/res/values/base-strings.xml b/samples/browseable/Flashlight/Application/res/values/base-strings.xml
deleted file mode 100644
index f0f3d9d..0000000
--- a/samples/browseable/Flashlight/Application/res/values/base-strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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">Flashlight</string>
- <string name="intro_message">
- <![CDATA[
-
-
- Wearable activity that uses your wearable screen as a flashlight. There is also
- a party-mode option, if you want to make things interesting.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/Flashlight/Application/res/values/template-dimens.xml b/samples/browseable/Flashlight/Application/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/Flashlight/Application/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/Flashlight/Application/res/values/template-styles.xml b/samples/browseable/Flashlight/Application/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593..0000000
--- a/samples/browseable/Flashlight/Application/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- 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/Flashlight/Application/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/Flashlight/Application/src/com.example.android.common/activities/SampleActivityBase.java
deleted file mode 100644
index 3228927..0000000
--- a/samples/browseable/Flashlight/Application/src/com.example.android.common/activities/SampleActivityBase.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-* 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.common.activities;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-
-import com.example.android.common.logger.Log;
-import com.example.android.common.logger.LogWrapper;
-
-/**
- * Base launcher activity, to handle most of the common plumbing for samples.
- */
-public class SampleActivityBase extends FragmentActivity {
-
- public static final String TAG = "SampleActivityBase";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- initializeLogging();
- }
-
- /** Set up targets to receive log data */
- public void initializeLogging() {
- // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
- // Wraps Android's native log framework
- LogWrapper logWrapper = new LogWrapper();
- Log.setLogNode(logWrapper);
-
- Log.i(TAG, "Ready");
- }
-}
diff --git a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/Log.java b/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/Log.java
deleted file mode 100644
index 17503c5..0000000
--- a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/Log.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-/**
- * Helper class for a list (or tree) of LoggerNodes.
- *
- * <p>When this is set as the head of the list,
- * an instance of it can function as a drop-in replacement for {@link android.util.Log}.
- * Most of the methods in this class server only to map a method call in Log to its equivalent
- * in LogNode.</p>
- */
-public class Log {
- // Grabbing the native values from Android's native logging facilities,
- // to make for easy migration and interop.
- public static final int NONE = -1;
- public static final int VERBOSE = android.util.Log.VERBOSE;
- public static final int DEBUG = android.util.Log.DEBUG;
- public static final int INFO = android.util.Log.INFO;
- public static final int WARN = android.util.Log.WARN;
- public static final int ERROR = android.util.Log.ERROR;
- public static final int ASSERT = android.util.Log.ASSERT;
-
- // Stores the beginning of the LogNode topology.
- private static LogNode mLogNode;
-
- /**
- * Returns the next LogNode in the linked list.
- */
- public static LogNode getLogNode() {
- return mLogNode;
- }
-
- /**
- * Sets the LogNode data will be sent to.
- */
- public static void setLogNode(LogNode node) {
- mLogNode = node;
- }
-
- /**
- * Instructs the LogNode to print the log data provided. Other LogNodes can
- * be chained to the end of the LogNode as desired.
- *
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void println(int priority, String tag, String msg, Throwable tr) {
- if (mLogNode != null) {
- mLogNode.println(priority, tag, msg, tr);
- }
- }
-
- /**
- * Instructs the LogNode to print the log data provided. Other LogNodes can
- * be chained to the end of the LogNode as desired.
- *
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- */
- public static void println(int priority, String tag, String msg) {
- println(priority, tag, msg, null);
- }
-
- /**
- * Prints a message at VERBOSE priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void v(String tag, String msg, Throwable tr) {
- println(VERBOSE, tag, msg, tr);
- }
-
- /**
- * Prints a message at VERBOSE priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void v(String tag, String msg) {
- v(tag, msg, null);
- }
-
-
- /**
- * Prints a message at DEBUG priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void d(String tag, String msg, Throwable tr) {
- println(DEBUG, tag, msg, tr);
- }
-
- /**
- * Prints a message at DEBUG priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void d(String tag, String msg) {
- d(tag, msg, null);
- }
-
- /**
- * Prints a message at INFO priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void i(String tag, String msg, Throwable tr) {
- println(INFO, tag, msg, tr);
- }
-
- /**
- * Prints a message at INFO priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void i(String tag, String msg) {
- i(tag, msg, null);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void w(String tag, String msg, Throwable tr) {
- println(WARN, tag, msg, tr);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void w(String tag, String msg) {
- w(tag, msg, null);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void w(String tag, Throwable tr) {
- w(tag, null, tr);
- }
-
- /**
- * Prints a message at ERROR priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void e(String tag, String msg, Throwable tr) {
- println(ERROR, tag, msg, tr);
- }
-
- /**
- * Prints a message at ERROR priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void e(String tag, String msg) {
- e(tag, msg, null);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void wtf(String tag, String msg, Throwable tr) {
- println(ASSERT, tag, msg, tr);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void wtf(String tag, String msg) {
- wtf(tag, msg, null);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void wtf(String tag, Throwable tr) {
- wtf(tag, null, tr);
- }
-}
diff --git a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogFragment.java
deleted file mode 100644
index b302acd..0000000
--- a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogFragment.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-* 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.
-*/
-/*
- * 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.common.logger;
-
-import android.graphics.Typeface;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ScrollView;
-
-/**
- * Simple fraggment which contains a LogView and uses is to output log data it receives
- * through the LogNode interface.
- */
-public class LogFragment extends Fragment {
-
- private LogView mLogView;
- private ScrollView mScrollView;
-
- public LogFragment() {}
-
- public View inflateViews() {
- mScrollView = new ScrollView(getActivity());
- ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- mScrollView.setLayoutParams(scrollParams);
-
- mLogView = new LogView(getActivity());
- ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams);
- logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- mLogView.setLayoutParams(logParams);
- mLogView.setClickable(true);
- mLogView.setFocusable(true);
- mLogView.setTypeface(Typeface.MONOSPACE);
-
- // Want to set padding as 16 dips, setPadding takes pixels. Hooray math!
- int paddingDips = 16;
- double scale = getResources().getDisplayMetrics().density;
- int paddingPixels = (int) ((paddingDips * (scale)) + .5);
- mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels);
- mLogView.setCompoundDrawablePadding(paddingPixels);
-
- mLogView.setGravity(Gravity.BOTTOM);
- mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium);
-
- mScrollView.addView(mLogView);
- return mScrollView;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
-
- View result = inflateViews();
-
- mLogView.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
- @Override
- public void afterTextChanged(Editable s) {
- mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
- }
- });
- return result;
- }
-
- public LogView getLogView() {
- return mLogView;
- }
-}
\ No newline at end of file
diff --git a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogNode.java b/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogNode.java
deleted file mode 100644
index bc37cab..0000000
--- a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogNode.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012 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.common.logger;
-
-/**
- * Basic interface for a logging system that can output to one or more targets.
- * Note that in addition to classes that will output these logs in some format,
- * one can also implement this interface over a filter and insert that in the chain,
- * such that no targets further down see certain data, or see manipulated forms of the data.
- * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data
- * it received to HTML and sent it along to the next node in the chain, without printing it
- * anywhere.
- */
-public interface LogNode {
-
- /**
- * Instructs first LogNode in the list to print the log data provided.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public void println(int priority, String tag, String msg, Throwable tr);
-
-}
diff --git a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogView.java b/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogView.java
deleted file mode 100644
index c01542b..0000000
--- a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogView.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-import android.app.Activity;
-import android.content.Context;
-import android.util.*;
-import android.widget.TextView;
-
-/** Simple TextView which is used to output log data received through the LogNode interface.
-*/
-public class LogView extends TextView implements LogNode {
-
- public LogView(Context context) {
- super(context);
- }
-
- public LogView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public LogView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- /**
- * Formats the log data and prints it out to the LogView.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
-
-
- String priorityStr = null;
-
- // For the purposes of this View, we want to print the priority as readable text.
- switch(priority) {
- case android.util.Log.VERBOSE:
- priorityStr = "VERBOSE";
- break;
- case android.util.Log.DEBUG:
- priorityStr = "DEBUG";
- break;
- case android.util.Log.INFO:
- priorityStr = "INFO";
- break;
- case android.util.Log.WARN:
- priorityStr = "WARN";
- break;
- case android.util.Log.ERROR:
- priorityStr = "ERROR";
- break;
- case android.util.Log.ASSERT:
- priorityStr = "ASSERT";
- break;
- default:
- break;
- }
-
- // Handily, the Log class has a facility for converting a stack trace into a usable string.
- String exceptionStr = null;
- if (tr != null) {
- exceptionStr = android.util.Log.getStackTraceString(tr);
- }
-
- // Take the priority, tag, message, and exception, and concatenate as necessary
- // into one usable line of text.
- final StringBuilder outputBuilder = new StringBuilder();
-
- String delimiter = "\t";
- appendIfNotNull(outputBuilder, priorityStr, delimiter);
- appendIfNotNull(outputBuilder, tag, delimiter);
- appendIfNotNull(outputBuilder, msg, delimiter);
- appendIfNotNull(outputBuilder, exceptionStr, delimiter);
-
- // In case this was originally called from an AsyncTask or some other off-UI thread,
- // make sure the update occurs within the UI thread.
- ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() {
- @Override
- public void run() {
- // Display the text we just generated within the LogView.
- appendToLog(outputBuilder.toString());
- }
- })));
-
- if (mNext != null) {
- mNext.println(priority, tag, msg, tr);
- }
- }
-
- public LogNode getNext() {
- return mNext;
- }
-
- public void setNext(LogNode node) {
- mNext = node;
- }
-
- /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since
- * the logger takes so many arguments that might be null, this method helps cut out some of the
- * agonizing tedium of writing the same 3 lines over and over.
- * @param source StringBuilder containing the text to append to.
- * @param addStr The String to append
- * @param delimiter The String to separate the source and appended strings. A tab or comma,
- * for instance.
- * @return The fully concatenated String as a StringBuilder
- */
- private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) {
- if (addStr != null) {
- if (addStr.length() == 0) {
- delimiter = "";
- }
-
- return source.append(addStr).append(delimiter);
- }
- return source;
- }
-
- // The next LogNode in the chain.
- LogNode mNext;
-
- /** Outputs the string as a new line of log data in the LogView. */
- public void appendToLog(String s) {
- append("\n" + s);
- }
-
-
-}
diff --git a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogWrapper.java
deleted file mode 100644
index 16a9e7b..0000000
--- a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/LogWrapper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012 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.common.logger;
-
-import android.util.Log;
-
-/**
- * Helper class which wraps Android's native Log utility in the Logger interface. This way
- * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously.
- */
-public class LogWrapper implements LogNode {
-
- // For piping: The next node to receive Log data after this one has done its work.
- private LogNode mNext;
-
- /**
- * Returns the next LogNode in the linked list.
- */
- public LogNode getNext() {
- return mNext;
- }
-
- /**
- * Sets the LogNode data will be sent to..
- */
- public void setNext(LogNode node) {
- mNext = node;
- }
-
- /**
- * Prints data out to the console using Android's native log mechanism.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
- // There actually are log methods that don't take a msg parameter. For now,
- // if that's the case, just convert null to the empty string and move on.
- String useMsg = msg;
- if (useMsg == null) {
- useMsg = "";
- }
-
- // If an exeption was provided, convert that exception to a usable string and attach
- // it to the end of the msg method.
- if (tr != null) {
- msg += "\n" + Log.getStackTraceString(tr);
- }
-
- // This is functionally identical to Log.x(tag, useMsg);
- // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg)
- Log.println(priority, tag, useMsg);
-
- // If this isn't the last node in the chain, move things along.
- if (mNext != null) {
- mNext.println(priority, tag, msg, tr);
- }
- }
-}
diff --git a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/MessageOnlyLogFilter.java
deleted file mode 100644
index 19967dc..0000000
--- a/samples/browseable/Flashlight/Application/src/com.example.android.common/logger/MessageOnlyLogFilter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-/**
- * Simple {@link LogNode} filter, removes everything except the message.
- * Useful for situations like on-screen log output where you don't want a lot of metadata displayed,
- * just easy-to-read message updates as they're happening.
- */
-public class MessageOnlyLogFilter implements LogNode {
-
- LogNode mNext;
-
- /**
- * Takes the "next" LogNode as a parameter, to simplify chaining.
- *
- * @param next The next LogNode in the pipeline.
- */
- public MessageOnlyLogFilter(LogNode next) {
- mNext = next;
- }
-
- public MessageOnlyLogFilter() {
- }
-
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
- if (mNext != null) {
- getNext().println(Log.NONE, null, msg, null);
- }
- }
-
- /**
- * Returns the next LogNode in the chain.
- */
- public LogNode getNext() {
- return mNext;
- }
-
- /**
- * Sets the LogNode data will be sent to..
- */
- public void setNext(LogNode node) {
- mNext = node;
- }
-
-}
diff --git a/samples/browseable/Flashlight/Wearable/res/drawable-hdpi/ic_launcher.png b/samples/browseable/Flashlight/Wearable/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/Flashlight/Wearable/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Wearable/res/drawable-mdpi/ic_launcher.png b/samples/browseable/Flashlight/Wearable/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/Flashlight/Wearable/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Wearable/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/Flashlight/Wearable/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/Flashlight/Wearable/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/Wearable/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/Flashlight/Wearable/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/Flashlight/Wearable/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Flashlight/res/drawable-hdpi/ic_launcher.png b/samples/browseable/Flashlight/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..4f298cf
--- /dev/null
+++ b/samples/browseable/Flashlight/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Flashlight/res/drawable-mdpi/ic_launcher.png b/samples/browseable/Flashlight/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17cff3c
--- /dev/null
+++ b/samples/browseable/Flashlight/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Flashlight/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/Flashlight/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..1663ae4
--- /dev/null
+++ b/samples/browseable/Flashlight/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Flashlight/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/Flashlight/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d3ea0c0
--- /dev/null
+++ b/samples/browseable/Flashlight/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Flashlight/Wearable/res/layout/main.xml b/samples/browseable/Flashlight/res/layout/main.xml
similarity index 100%
rename from samples/browseable/Flashlight/Wearable/res/layout/main.xml
rename to samples/browseable/Flashlight/res/layout/main.xml
diff --git a/samples/browseable/Flashlight/Wearable/res/layout/party_light.xml b/samples/browseable/Flashlight/res/layout/party_light.xml
similarity index 100%
rename from samples/browseable/Flashlight/Wearable/res/layout/party_light.xml
rename to samples/browseable/Flashlight/res/layout/party_light.xml
diff --git a/samples/browseable/Flashlight/Wearable/res/layout/white_light.xml b/samples/browseable/Flashlight/res/layout/white_light.xml
similarity index 100%
rename from samples/browseable/Flashlight/Wearable/res/layout/white_light.xml
rename to samples/browseable/Flashlight/res/layout/white_light.xml
diff --git a/samples/browseable/Flashlight/Wearable/res/values/strings.xml b/samples/browseable/Flashlight/res/values/strings.xml
similarity index 100%
rename from samples/browseable/Flashlight/Wearable/res/values/strings.xml
rename to samples/browseable/Flashlight/res/values/strings.xml
diff --git a/samples/browseable/Flashlight/Wearable/src/com.example.android.wearable.flashlight/MainActivity.java b/samples/browseable/Flashlight/src/com.example.android.wearable.flashlight/MainActivity.java
similarity index 100%
rename from samples/browseable/Flashlight/Wearable/src/com.example.android.wearable.flashlight/MainActivity.java
rename to samples/browseable/Flashlight/src/com.example.android.wearable.flashlight/MainActivity.java
diff --git a/samples/browseable/Flashlight/Wearable/src/com.example.android.wearable.flashlight/PartyLightView.java b/samples/browseable/Flashlight/src/com.example.android.wearable.flashlight/PartyLightView.java
similarity index 100%
rename from samples/browseable/Flashlight/Wearable/src/com.example.android.wearable.flashlight/PartyLightView.java
rename to samples/browseable/Flashlight/src/com.example.android.wearable.flashlight/PartyLightView.java
diff --git a/samples/browseable/FragmentTransition/AndroidManifest.xml b/samples/browseable/FragmentTransition/AndroidManifest.xml
deleted file mode 100644
index 2cfe406..0000000
--- a/samples/browseable/FragmentTransition/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2014 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.fragmenttransition"
- android:versionCode="1"
- android:versionName="1.0">
-
- <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle -->
-
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name="com.example.android.fragmenttransition.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/FragmentTransition/_index.jd b/samples/browseable/FragmentTransition/_index.jd
deleted file mode 100644
index afab623..0000000
--- a/samples/browseable/FragmentTransition/_index.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.tags="FragmentTransition"
-sample.group=UI
-@jd:body
-
-<p>
-
- This sample demonstrates how to start a transition right after a fragment transaction.
-
- </p>
diff --git a/samples/browseable/FragmentTransition/res/drawable-hdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index dfa1b45..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/FragmentTransition/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-mdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 5f4ae7b..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p1.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p1.jpg
deleted file mode 100644
index 10f07ac..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p1.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p10.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p10.jpg
deleted file mode 100644
index 4272f4c..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p10.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p11.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p11.jpg
deleted file mode 100644
index c5722b2..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p11.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p2.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p2.jpg
deleted file mode 100644
index ca380ae..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p2.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p3.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p3.jpg
deleted file mode 100644
index 6fc71e7..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p3.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p4.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p4.jpg
deleted file mode 100644
index 153c1ff..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p4.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p5.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p5.jpg
deleted file mode 100644
index 46d6a13..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p5.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p6.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p6.jpg
deleted file mode 100644
index 89ccb83..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p6.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p7.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p7.jpg
deleted file mode 100644
index 7e9546d..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p7.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p8.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p8.jpg
deleted file mode 100644
index 21e25ba..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p8.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-nodpi/p9.jpg b/samples/browseable/FragmentTransition/res/drawable-nodpi/p9.jpg
deleted file mode 100644
index 79854cb..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-nodpi/p9.jpg
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 5e00f33..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/FragmentTransition/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index e061498..0000000
--- a/samples/browseable/FragmentTransition/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/FragmentTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/FragmentTransition/res/layout-w720dp/activity_main.xml
deleted file mode 100755
index c9a52f6..0000000
--- a/samples/browseable/FragmentTransition/res/layout-w720dp/activity_main.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<!--
- 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="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/sample_main_layout">
-
- <LinearLayout
- android:id="@+id/sample_output"
- android:layout_width="0px"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="vertical">
-
- <FrameLayout
- style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <TextView
- style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/margin_medium"
- android:paddingRight="@dimen/margin_medium"
- android:paddingTop="@dimen/margin_large"
- android:paddingBottom="@dimen/margin_large"
- android:text="@string/intro_message" />
- </FrameLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@android:color/darker_gray" />
-
- <fragment
- android:name="com.example.android.common.logger.LogFragment"
- android:id="@+id/log_fragment"
- android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_weight="1" />
-
- </LinearLayout>
-
- <View
- android:layout_width="1dp"
- android:layout_height="match_parent"
- android:background="@android:color/darker_gray" />
-
- <FrameLayout
- android:id="@+id/sample_content_fragment"
- android:layout_weight="2"
- android:layout_width="0px"
- android:layout_height="match_parent" />
-
-</LinearLayout>
-
-
diff --git a/samples/browseable/FragmentTransition/res/layout/activity_main.xml b/samples/browseable/FragmentTransition/res/layout/activity_main.xml
deleted file mode 100755
index 1ae4f98..0000000
--- a/samples/browseable/FragmentTransition/res/layout/activity_main.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<!--
- 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="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/sample_main_layout">
-
- <ViewAnimator
- android:id="@+id/sample_output"
- android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_weight="1">
-
- <ScrollView
- style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/horizontal_page_margin"
- android:paddingRight="@dimen/horizontal_page_margin"
- android:paddingTop="@dimen/vertical_page_margin"
- android:paddingBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </ScrollView>
-
- <fragment
- android:name="com.example.android.common.logger.LogFragment"
- android:id="@+id/log_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- </ViewAnimator>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@android:color/darker_gray" />
-
- <FrameLayout
- android:id="@+id/sample_content_fragment"
- android:layout_weight="2"
- android:layout_width="match_parent"
- android:layout_height="0px" />
-
-</LinearLayout>
-
diff --git a/samples/browseable/FragmentTransition/res/layout/fragment_detail_content.xml b/samples/browseable/FragmentTransition/res/layout/fragment_detail_content.xml
deleted file mode 100644
index 2068460..0000000
--- a/samples/browseable/FragmentTransition/res/layout/fragment_detail_content.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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.
--->
-<ScrollView
- android:id="@+id/frame"
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbars="none">
-
- <RelativeLayout
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/image"
- android:layout_width="match_parent"
- android:layout_height="180dp"
- android:scaleType="centerCrop"
- tools:src="@drawable/p1"/>
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@id/image"
- android:layout_alignEnd="@id/image"
- android:layout_marginEnd="16dp"
- android:shadowColor="#000000"
- android:shadowDx="0"
- android:shadowDy="0"
- android:shadowRadius="10"
- android:textColor="#ffffff"
- android:textSize="24sp"
- android:textStyle="bold"
- tools:text="Image"/>
-
- <TextView
- android:id="@+id/body"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/image"
- android:layout_marginBottom="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp"
- android:text="@string/lorem_ipsum"/>
-
- </RelativeLayout>
-
-</ScrollView>
\ No newline at end of file
diff --git a/samples/browseable/FragmentTransition/res/layout/fragment_fragment_transition.xml b/samples/browseable/FragmentTransition/res/layout/fragment_fragment_transition.xml
deleted file mode 100644
index 6e1c7a1..0000000
--- a/samples/browseable/FragmentTransition/res/layout/fragment_fragment_transition.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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.
--->
-<GridView
- android:id="@+id/grid"
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:columnWidth="150dp"
- android:horizontalSpacing="1dp"
- android:numColumns="auto_fit"
- android:padding="1dp"
- android:scrollbars="none"
- android:stretchMode="columnWidth"
- android:verticalSpacing="1dp"
- tools:context="com.example.android.fragmenttransition.FragmentTransitionFragment"/>
diff --git a/samples/browseable/FragmentTransition/res/layout/item_meat_grid.xml b/samples/browseable/FragmentTransition/res/layout/item_meat_grid.xml
deleted file mode 100644
index df34883..0000000
--- a/samples/browseable/FragmentTransition/res/layout/item_meat_grid.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2014 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.
--->
-<FrameLayout
- android:id="@+id/frame"
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <RelativeLayout
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="150dp"
- tools:ignore="UselessParent">
-
- <ImageView
- android:id="@+id/image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"
- tools:src="@drawable/p1"/>
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="16dp"
- android:layout_marginStart="16dp"
- android:gravity="center_horizontal"
- android:shadowColor="#000000"
- android:shadowDx="0"
- android:shadowDy="0"
- android:shadowRadius="10"
- android:textColor="#ffffff"
- android:textSize="24sp"
- android:textStyle="bold"
- tools:text="Hello"/>
-
- </RelativeLayout>
-
-</FrameLayout>
diff --git a/samples/browseable/FragmentTransition/res/menu/main.xml b/samples/browseable/FragmentTransition/res/menu/main.xml
deleted file mode 100644
index b49c2c5..0000000
--- a/samples/browseable/FragmentTransition/res/menu/main.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!--
- 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.
- -->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/menu_toggle_log"
- android:showAsAction="always"
- android:title="@string/sample_show_log" />
-</menu>
diff --git a/samples/browseable/FragmentTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/FragmentTransition/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2..0000000
--- a/samples/browseable/FragmentTransition/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- 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/FragmentTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/FragmentTransition/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/FragmentTransition/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/FragmentTransition/res/values-v11/template-styles.xml b/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66..0000000
--- a/samples/browseable/FragmentTransition/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- 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/FragmentTransition/res/values-v21/base-colors.xml b/samples/browseable/FragmentTransition/res/values-v21/base-colors.xml
deleted file mode 100644
index 34c9cd1..0000000
--- a/samples/browseable/FragmentTransition/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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>
-
-
-</resources>
diff --git a/samples/browseable/FragmentTransition/res/values-v21/base-template-styles.xml b/samples/browseable/FragmentTransition/res/values-v21/base-template-styles.xml
deleted file mode 100644
index 0b2948f..0000000
--- a/samples/browseable/FragmentTransition/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
- </style>
-
-</resources>
diff --git a/samples/browseable/FragmentTransition/res/values/base-strings.xml b/samples/browseable/FragmentTransition/res/values/base-strings.xml
deleted file mode 100644
index 92707c9..0000000
--- a/samples/browseable/FragmentTransition/res/values/base-strings.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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">FragmentTransition</string>
- <string name="intro_message">
- <![CDATA[
-
-
- This sample demonstrates how to start a transition right after a fragment transaction.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/FragmentTransition/res/values/fragmentview_strings.xml b/samples/browseable/FragmentTransition/res/values/fragmentview_strings.xml
deleted file mode 100755
index 7b9d9ec..0000000
--- a/samples/browseable/FragmentTransition/res/values/fragmentview_strings.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<!--
- 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="sample_show_log">Show Log</string>
- <string name="sample_hide_log">Hide Log</string>
-</resources>
diff --git a/samples/browseable/FragmentTransition/res/values/strings.xml b/samples/browseable/FragmentTransition/res/values/strings.xml
deleted file mode 100644
index 5f77789..0000000
--- a/samples/browseable/FragmentTransition/res/values/strings.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2014 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="lorem_ipsum">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</string>
-
-</resources>
diff --git a/samples/browseable/FragmentTransition/res/values/template-dimens.xml b/samples/browseable/FragmentTransition/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/FragmentTransition/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/FragmentTransition/res/values/template-styles.xml b/samples/browseable/FragmentTransition/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593..0000000
--- a/samples/browseable/FragmentTransition/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- 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/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java
deleted file mode 100644
index 3228927..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/activities/SampleActivityBase.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-* 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.common.activities;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-
-import com.example.android.common.logger.Log;
-import com.example.android.common.logger.LogWrapper;
-
-/**
- * Base launcher activity, to handle most of the common plumbing for samples.
- */
-public class SampleActivityBase extends FragmentActivity {
-
- public static final String TAG = "SampleActivityBase";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- initializeLogging();
- }
-
- /** Set up targets to receive log data */
- public void initializeLogging() {
- // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
- // Wraps Android's native log framework
- LogWrapper logWrapper = new LogWrapper();
- Log.setLogNode(logWrapper);
-
- Log.i(TAG, "Ready");
- }
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java
deleted file mode 100644
index 17503c5..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/Log.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-/**
- * Helper class for a list (or tree) of LoggerNodes.
- *
- * <p>When this is set as the head of the list,
- * an instance of it can function as a drop-in replacement for {@link android.util.Log}.
- * Most of the methods in this class server only to map a method call in Log to its equivalent
- * in LogNode.</p>
- */
-public class Log {
- // Grabbing the native values from Android's native logging facilities,
- // to make for easy migration and interop.
- public static final int NONE = -1;
- public static final int VERBOSE = android.util.Log.VERBOSE;
- public static final int DEBUG = android.util.Log.DEBUG;
- public static final int INFO = android.util.Log.INFO;
- public static final int WARN = android.util.Log.WARN;
- public static final int ERROR = android.util.Log.ERROR;
- public static final int ASSERT = android.util.Log.ASSERT;
-
- // Stores the beginning of the LogNode topology.
- private static LogNode mLogNode;
-
- /**
- * Returns the next LogNode in the linked list.
- */
- public static LogNode getLogNode() {
- return mLogNode;
- }
-
- /**
- * Sets the LogNode data will be sent to.
- */
- public static void setLogNode(LogNode node) {
- mLogNode = node;
- }
-
- /**
- * Instructs the LogNode to print the log data provided. Other LogNodes can
- * be chained to the end of the LogNode as desired.
- *
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void println(int priority, String tag, String msg, Throwable tr) {
- if (mLogNode != null) {
- mLogNode.println(priority, tag, msg, tr);
- }
- }
-
- /**
- * Instructs the LogNode to print the log data provided. Other LogNodes can
- * be chained to the end of the LogNode as desired.
- *
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- */
- public static void println(int priority, String tag, String msg) {
- println(priority, tag, msg, null);
- }
-
- /**
- * Prints a message at VERBOSE priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void v(String tag, String msg, Throwable tr) {
- println(VERBOSE, tag, msg, tr);
- }
-
- /**
- * Prints a message at VERBOSE priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void v(String tag, String msg) {
- v(tag, msg, null);
- }
-
-
- /**
- * Prints a message at DEBUG priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void d(String tag, String msg, Throwable tr) {
- println(DEBUG, tag, msg, tr);
- }
-
- /**
- * Prints a message at DEBUG priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void d(String tag, String msg) {
- d(tag, msg, null);
- }
-
- /**
- * Prints a message at INFO priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void i(String tag, String msg, Throwable tr) {
- println(INFO, tag, msg, tr);
- }
-
- /**
- * Prints a message at INFO priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void i(String tag, String msg) {
- i(tag, msg, null);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void w(String tag, String msg, Throwable tr) {
- println(WARN, tag, msg, tr);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void w(String tag, String msg) {
- w(tag, msg, null);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void w(String tag, Throwable tr) {
- w(tag, null, tr);
- }
-
- /**
- * Prints a message at ERROR priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void e(String tag, String msg, Throwable tr) {
- println(ERROR, tag, msg, tr);
- }
-
- /**
- * Prints a message at ERROR priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void e(String tag, String msg) {
- e(tag, msg, null);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void wtf(String tag, String msg, Throwable tr) {
- println(ASSERT, tag, msg, tr);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void wtf(String tag, String msg) {
- wtf(tag, msg, null);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void wtf(String tag, Throwable tr) {
- wtf(tag, null, tr);
- }
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java
deleted file mode 100644
index b302acd..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogFragment.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-* 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.
-*/
-/*
- * 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.common.logger;
-
-import android.graphics.Typeface;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ScrollView;
-
-/**
- * Simple fraggment which contains a LogView and uses is to output log data it receives
- * through the LogNode interface.
- */
-public class LogFragment extends Fragment {
-
- private LogView mLogView;
- private ScrollView mScrollView;
-
- public LogFragment() {}
-
- public View inflateViews() {
- mScrollView = new ScrollView(getActivity());
- ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- mScrollView.setLayoutParams(scrollParams);
-
- mLogView = new LogView(getActivity());
- ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams);
- logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- mLogView.setLayoutParams(logParams);
- mLogView.setClickable(true);
- mLogView.setFocusable(true);
- mLogView.setTypeface(Typeface.MONOSPACE);
-
- // Want to set padding as 16 dips, setPadding takes pixels. Hooray math!
- int paddingDips = 16;
- double scale = getResources().getDisplayMetrics().density;
- int paddingPixels = (int) ((paddingDips * (scale)) + .5);
- mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels);
- mLogView.setCompoundDrawablePadding(paddingPixels);
-
- mLogView.setGravity(Gravity.BOTTOM);
- mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium);
-
- mScrollView.addView(mLogView);
- return mScrollView;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
-
- View result = inflateViews();
-
- mLogView.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
- @Override
- public void afterTextChanged(Editable s) {
- mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
- }
- });
- return result;
- }
-
- public LogView getLogView() {
- return mLogView;
- }
-}
\ No newline at end of file
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java
deleted file mode 100644
index bc37cab..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogNode.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012 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.common.logger;
-
-/**
- * Basic interface for a logging system that can output to one or more targets.
- * Note that in addition to classes that will output these logs in some format,
- * one can also implement this interface over a filter and insert that in the chain,
- * such that no targets further down see certain data, or see manipulated forms of the data.
- * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data
- * it received to HTML and sent it along to the next node in the chain, without printing it
- * anywhere.
- */
-public interface LogNode {
-
- /**
- * Instructs first LogNode in the list to print the log data provided.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public void println(int priority, String tag, String msg, Throwable tr);
-
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java
deleted file mode 100644
index c01542b..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogView.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-import android.app.Activity;
-import android.content.Context;
-import android.util.*;
-import android.widget.TextView;
-
-/** Simple TextView which is used to output log data received through the LogNode interface.
-*/
-public class LogView extends TextView implements LogNode {
-
- public LogView(Context context) {
- super(context);
- }
-
- public LogView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public LogView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- /**
- * Formats the log data and prints it out to the LogView.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
-
-
- String priorityStr = null;
-
- // For the purposes of this View, we want to print the priority as readable text.
- switch(priority) {
- case android.util.Log.VERBOSE:
- priorityStr = "VERBOSE";
- break;
- case android.util.Log.DEBUG:
- priorityStr = "DEBUG";
- break;
- case android.util.Log.INFO:
- priorityStr = "INFO";
- break;
- case android.util.Log.WARN:
- priorityStr = "WARN";
- break;
- case android.util.Log.ERROR:
- priorityStr = "ERROR";
- break;
- case android.util.Log.ASSERT:
- priorityStr = "ASSERT";
- break;
- default:
- break;
- }
-
- // Handily, the Log class has a facility for converting a stack trace into a usable string.
- String exceptionStr = null;
- if (tr != null) {
- exceptionStr = android.util.Log.getStackTraceString(tr);
- }
-
- // Take the priority, tag, message, and exception, and concatenate as necessary
- // into one usable line of text.
- final StringBuilder outputBuilder = new StringBuilder();
-
- String delimiter = "\t";
- appendIfNotNull(outputBuilder, priorityStr, delimiter);
- appendIfNotNull(outputBuilder, tag, delimiter);
- appendIfNotNull(outputBuilder, msg, delimiter);
- appendIfNotNull(outputBuilder, exceptionStr, delimiter);
-
- // In case this was originally called from an AsyncTask or some other off-UI thread,
- // make sure the update occurs within the UI thread.
- ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() {
- @Override
- public void run() {
- // Display the text we just generated within the LogView.
- appendToLog(outputBuilder.toString());
- }
- })));
-
- if (mNext != null) {
- mNext.println(priority, tag, msg, tr);
- }
- }
-
- public LogNode getNext() {
- return mNext;
- }
-
- public void setNext(LogNode node) {
- mNext = node;
- }
-
- /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since
- * the logger takes so many arguments that might be null, this method helps cut out some of the
- * agonizing tedium of writing the same 3 lines over and over.
- * @param source StringBuilder containing the text to append to.
- * @param addStr The String to append
- * @param delimiter The String to separate the source and appended strings. A tab or comma,
- * for instance.
- * @return The fully concatenated String as a StringBuilder
- */
- private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) {
- if (addStr != null) {
- if (addStr.length() == 0) {
- delimiter = "";
- }
-
- return source.append(addStr).append(delimiter);
- }
- return source;
- }
-
- // The next LogNode in the chain.
- LogNode mNext;
-
- /** Outputs the string as a new line of log data in the LogView. */
- public void appendToLog(String s) {
- append("\n" + s);
- }
-
-
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java
deleted file mode 100644
index 16a9e7b..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/LogWrapper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012 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.common.logger;
-
-import android.util.Log;
-
-/**
- * Helper class which wraps Android's native Log utility in the Logger interface. This way
- * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously.
- */
-public class LogWrapper implements LogNode {
-
- // For piping: The next node to receive Log data after this one has done its work.
- private LogNode mNext;
-
- /**
- * Returns the next LogNode in the linked list.
- */
- public LogNode getNext() {
- return mNext;
- }
-
- /**
- * Sets the LogNode data will be sent to..
- */
- public void setNext(LogNode node) {
- mNext = node;
- }
-
- /**
- * Prints data out to the console using Android's native log mechanism.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
- // There actually are log methods that don't take a msg parameter. For now,
- // if that's the case, just convert null to the empty string and move on.
- String useMsg = msg;
- if (useMsg == null) {
- useMsg = "";
- }
-
- // If an exeption was provided, convert that exception to a usable string and attach
- // it to the end of the msg method.
- if (tr != null) {
- msg += "\n" + Log.getStackTraceString(tr);
- }
-
- // This is functionally identical to Log.x(tag, useMsg);
- // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg)
- Log.println(priority, tag, useMsg);
-
- // If this isn't the last node in the chain, move things along.
- if (mNext != null) {
- mNext.println(priority, tag, msg, tr);
- }
- }
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
deleted file mode 100644
index 19967dc..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-/**
- * Simple {@link LogNode} filter, removes everything except the message.
- * Useful for situations like on-screen log output where you don't want a lot of metadata displayed,
- * just easy-to-read message updates as they're happening.
- */
-public class MessageOnlyLogFilter implements LogNode {
-
- LogNode mNext;
-
- /**
- * Takes the "next" LogNode as a parameter, to simplify chaining.
- *
- * @param next The next LogNode in the pipeline.
- */
- public MessageOnlyLogFilter(LogNode next) {
- mNext = next;
- }
-
- public MessageOnlyLogFilter() {
- }
-
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
- if (mNext != null) {
- getNext().println(Log.NONE, null, msg, null);
- }
- }
-
- /**
- * Returns the next LogNode in the chain.
- */
- public LogNode getNext() {
- return mNext;
- }
-
- /**
- * Sets the LogNode data will be sent to..
- */
- public void setNext(LogNode node) {
- mNext = node;
- }
-
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java
deleted file mode 100644
index 20049e3..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabLayout.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * 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.
- */
-
-package com.example.android.common.view;
-
-import android.content.Context;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.HorizontalScrollView;
-import android.widget.TextView;
-
-/**
- * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
- * the user's scroll progress.
- * <p>
- * To use the component, simply add it to your view hierarchy. Then in your
- * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
- * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
- * <p>
- * The colors can be customized in two ways. The first and simplest is to provide an array of colors
- * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
- * alternative is via the {@link TabColorizer} interface which provides you complete control over
- * which color is used for any individual position.
- * <p>
- * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
- * providing the layout ID of your custom layout.
- */
-public class SlidingTabLayout extends HorizontalScrollView {
-
- /**
- * Allows complete control over the colors drawn in the tab layout. Set with
- * {@link #setCustomTabColorizer(TabColorizer)}.
- */
- public interface TabColorizer {
-
- /**
- * @return return the color of the indicator used when {@code position} is selected.
- */
- int getIndicatorColor(int position);
-
- /**
- * @return return the color of the divider drawn to the right of {@code position}.
- */
- int getDividerColor(int position);
-
- }
-
- private static final int TITLE_OFFSET_DIPS = 24;
- private static final int TAB_VIEW_PADDING_DIPS = 16;
- private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
-
- private int mTitleOffset;
-
- private int mTabViewLayoutId;
- private int mTabViewTextViewId;
-
- private ViewPager mViewPager;
- private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
-
- private final SlidingTabStrip mTabStrip;
-
- public SlidingTabLayout(Context context) {
- this(context, null);
- }
-
- public SlidingTabLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- // Disable the Scroll Bar
- setHorizontalScrollBarEnabled(false);
- // Make sure that the Tab Strips fills this View
- setFillViewport(true);
-
- mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
-
- mTabStrip = new SlidingTabStrip(context);
- addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
- }
-
- /**
- * Set the custom {@link TabColorizer} to be used.
- *
- * If you only require simple custmisation then you can use
- * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
- * similar effects.
- */
- public void setCustomTabColorizer(TabColorizer tabColorizer) {
- mTabStrip.setCustomTabColorizer(tabColorizer);
- }
-
- /**
- * Sets the colors to be used for indicating the selected tab. These colors are treated as a
- * circular array. Providing one color will mean that all tabs are indicated with the same color.
- */
- public void setSelectedIndicatorColors(int... colors) {
- mTabStrip.setSelectedIndicatorColors(colors);
- }
-
- /**
- * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
- * Providing one color will mean that all tabs are indicated with the same color.
- */
- public void setDividerColors(int... colors) {
- mTabStrip.setDividerColors(colors);
- }
-
- /**
- * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
- * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
- * that the layout can update it's scroll position correctly.
- *
- * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
- */
- public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
- mViewPagerPageChangeListener = listener;
- }
-
- /**
- * Set the custom layout to be inflated for the tab views.
- *
- * @param layoutResId Layout id to be inflated
- * @param textViewId id of the {@link TextView} in the inflated view
- */
- public void setCustomTabView(int layoutResId, int textViewId) {
- mTabViewLayoutId = layoutResId;
- mTabViewTextViewId = textViewId;
- }
-
- /**
- * Sets the associated view pager. Note that the assumption here is that the pager content
- * (number of tabs and tab titles) does not change after this call has been made.
- */
- public void setViewPager(ViewPager viewPager) {
- mTabStrip.removeAllViews();
-
- mViewPager = viewPager;
- if (viewPager != null) {
- viewPager.setOnPageChangeListener(new InternalViewPagerListener());
- populateTabStrip();
- }
- }
-
- /**
- * Create a default view to be used for tabs. This is called if a custom tab view is not set via
- * {@link #setCustomTabView(int, int)}.
- */
- protected TextView createDefaultTabView(Context context) {
- TextView textView = new TextView(context);
- textView.setGravity(Gravity.CENTER);
- textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
- textView.setTypeface(Typeface.DEFAULT_BOLD);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- // If we're running on Honeycomb or newer, then we can use the Theme's
- // selectableItemBackground to ensure that the View has a pressed state
- TypedValue outValue = new TypedValue();
- getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
- outValue, true);
- textView.setBackgroundResource(outValue.resourceId);
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
- textView.setAllCaps(true);
- }
-
- int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
- textView.setPadding(padding, padding, padding, padding);
-
- return textView;
- }
-
- private void populateTabStrip() {
- final PagerAdapter adapter = mViewPager.getAdapter();
- final View.OnClickListener tabClickListener = new TabClickListener();
-
- for (int i = 0; i < adapter.getCount(); i++) {
- View tabView = null;
- TextView tabTitleView = null;
-
- if (mTabViewLayoutId != 0) {
- // If there is a custom tab view layout id set, try and inflate it
- tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
- false);
- tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
- }
-
- if (tabView == null) {
- tabView = createDefaultTabView(getContext());
- }
-
- if (tabTitleView == null && TextView.class.isInstance(tabView)) {
- tabTitleView = (TextView) tabView;
- }
-
- tabTitleView.setText(adapter.getPageTitle(i));
- tabView.setOnClickListener(tabClickListener);
-
- mTabStrip.addView(tabView);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mViewPager != null) {
- scrollToTab(mViewPager.getCurrentItem(), 0);
- }
- }
-
- private void scrollToTab(int tabIndex, int positionOffset) {
- final int tabStripChildCount = mTabStrip.getChildCount();
- if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
- return;
- }
-
- View selectedChild = mTabStrip.getChildAt(tabIndex);
- if (selectedChild != null) {
- int targetScrollX = selectedChild.getLeft() + positionOffset;
-
- if (tabIndex > 0 || positionOffset > 0) {
- // If we're not at the first child and are mid-scroll, make sure we obey the offset
- targetScrollX -= mTitleOffset;
- }
-
- scrollTo(targetScrollX, 0);
- }
- }
-
- private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
- private int mScrollState;
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- int tabStripChildCount = mTabStrip.getChildCount();
- if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
- return;
- }
-
- mTabStrip.onViewPagerPageChanged(position, positionOffset);
-
- View selectedTitle = mTabStrip.getChildAt(position);
- int extraOffset = (selectedTitle != null)
- ? (int) (positionOffset * selectedTitle.getWidth())
- : 0;
- scrollToTab(position, extraOffset);
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
- positionOffsetPixels);
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mScrollState = state;
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageScrollStateChanged(state);
- }
- }
-
- @Override
- public void onPageSelected(int position) {
- if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
- mTabStrip.onViewPagerPageChanged(position, 0f);
- scrollToTab(position, 0);
- }
-
- if (mViewPagerPageChangeListener != null) {
- mViewPagerPageChangeListener.onPageSelected(position);
- }
- }
-
- }
-
- private class TabClickListener implements View.OnClickListener {
- @Override
- public void onClick(View v) {
- for (int i = 0; i < mTabStrip.getChildCount(); i++) {
- if (v == mTabStrip.getChildAt(i)) {
- mViewPager.setCurrentItem(i);
- return;
- }
- }
- }
- }
-
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java b/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java
deleted file mode 100644
index d5bbbae..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.common/view/SlidingTabStrip.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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.
- */
-
-package com.example.android.common.view;
-
-import android.R;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.View;
-import android.widget.LinearLayout;
-
-class SlidingTabStrip extends LinearLayout {
-
- private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
- private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
- private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
- private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
-
- private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
- private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
- private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
-
- private final int mBottomBorderThickness;
- private final Paint mBottomBorderPaint;
-
- private final int mSelectedIndicatorThickness;
- private final Paint mSelectedIndicatorPaint;
-
- private final int mDefaultBottomBorderColor;
-
- private final Paint mDividerPaint;
- private final float mDividerHeight;
-
- private int mSelectedPosition;
- private float mSelectionOffset;
-
- private SlidingTabLayout.TabColorizer mCustomTabColorizer;
- private final SimpleTabColorizer mDefaultTabColorizer;
-
- SlidingTabStrip(Context context) {
- this(context, null);
- }
-
- SlidingTabStrip(Context context, AttributeSet attrs) {
- super(context, attrs);
- setWillNotDraw(false);
-
- final float density = getResources().getDisplayMetrics().density;
-
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
- final int themeForegroundColor = outValue.data;
-
- mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
- DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
-
- mDefaultTabColorizer = new SimpleTabColorizer();
- mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
- mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
- DEFAULT_DIVIDER_COLOR_ALPHA));
-
- mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
- mBottomBorderPaint = new Paint();
- mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
-
- mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
- mSelectedIndicatorPaint = new Paint();
-
- mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
- mDividerPaint = new Paint();
- mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
- }
-
- void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
- mCustomTabColorizer = customTabColorizer;
- invalidate();
- }
-
- void setSelectedIndicatorColors(int... colors) {
- // Make sure that the custom colorizer is removed
- mCustomTabColorizer = null;
- mDefaultTabColorizer.setIndicatorColors(colors);
- invalidate();
- }
-
- void setDividerColors(int... colors) {
- // Make sure that the custom colorizer is removed
- mCustomTabColorizer = null;
- mDefaultTabColorizer.setDividerColors(colors);
- invalidate();
- }
-
- void onViewPagerPageChanged(int position, float positionOffset) {
- mSelectedPosition = position;
- mSelectionOffset = positionOffset;
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- final int height = getHeight();
- final int childCount = getChildCount();
- final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
- final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
- ? mCustomTabColorizer
- : mDefaultTabColorizer;
-
- // Thick colored underline below the current selection
- if (childCount > 0) {
- View selectedTitle = getChildAt(mSelectedPosition);
- int left = selectedTitle.getLeft();
- int right = selectedTitle.getRight();
- int color = tabColorizer.getIndicatorColor(mSelectedPosition);
-
- if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
- int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
- if (color != nextColor) {
- color = blendColors(nextColor, color, mSelectionOffset);
- }
-
- // Draw the selection partway between the tabs
- View nextTitle = getChildAt(mSelectedPosition + 1);
- left = (int) (mSelectionOffset * nextTitle.getLeft() +
- (1.0f - mSelectionOffset) * left);
- right = (int) (mSelectionOffset * nextTitle.getRight() +
- (1.0f - mSelectionOffset) * right);
- }
-
- mSelectedIndicatorPaint.setColor(color);
-
- canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
- height, mSelectedIndicatorPaint);
- }
-
- // Thin underline along the entire bottom edge
- canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
-
- // Vertical separators between the titles
- int separatorTop = (height - dividerHeightPx) / 2;
- for (int i = 0; i < childCount - 1; i++) {
- View child = getChildAt(i);
- mDividerPaint.setColor(tabColorizer.getDividerColor(i));
- canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
- separatorTop + dividerHeightPx, mDividerPaint);
- }
- }
-
- /**
- * Set the alpha value of the {@code color} to be the given {@code alpha} value.
- */
- private static int setColorAlpha(int color, byte alpha) {
- return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
- }
-
- /**
- * Blend {@code color1} and {@code color2} using the given ratio.
- *
- * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
- * 0.0 will return {@code color2}.
- */
- private static int blendColors(int color1, int color2, float ratio) {
- final float inverseRation = 1f - ratio;
- float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
- float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
- float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
- return Color.rgb((int) r, (int) g, (int) b);
- }
-
- private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
- private int[] mIndicatorColors;
- private int[] mDividerColors;
-
- @Override
- public final int getIndicatorColor(int position) {
- return mIndicatorColors[position % mIndicatorColors.length];
- }
-
- @Override
- public final int getDividerColor(int position) {
- return mDividerColors[position % mDividerColors.length];
- }
-
- void setIndicatorColors(int... colors) {
- mIndicatorColors = colors;
- }
-
- void setDividerColors(int... colors) {
- mDividerColors = colors;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java
deleted file mode 100644
index 81e7b46..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/DetailFragment.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2014 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.fragmenttransition;
-
-import com.example.android.common.logger.Log;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.transition.Scene;
-import android.transition.TransitionManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-public class DetailFragment extends Fragment implements Animation.AnimationListener {
-
- private static final String TAG = "DetailFragment";
-
- private static final String ARG_RESOURCE_ID = "resource_id";
- private static final String ARG_TITLE = "title";
- private static final String ARG_X = "x";
- private static final String ARG_Y = "y";
- private static final String ARG_WIDTH = "width";
- private static final String ARG_HEIGHT = "height";
-
- /**
- * Create a new instance of DetailFragment.
- *
- * @param resourceId The resource ID of the Drawable image to show
- * @param title The title of the image
- * @param x The horizontal position of the grid item in pixel
- * @param y The vertical position of the grid item in pixel
- * @param width The width of the grid item in pixel
- * @param height The height of the grid item in pixel
- * @return a new instance of DetailFragment
- */
- public static DetailFragment newInstance(int resourceId, String title,
- int x, int y, int width, int height) {
- DetailFragment fragment = new DetailFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_RESOURCE_ID, resourceId);
- args.putString(ARG_TITLE, title);
- args.putInt(ARG_X, x);
- args.putInt(ARG_Y, y);
- args.putInt(ARG_WIDTH, width);
- args.putInt(ARG_HEIGHT, height);
- fragment.setArguments(args);
- return fragment;
- }
-
- public DetailFragment() {
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_detail, container, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- FrameLayout root = (FrameLayout) view;
- Context context = view.getContext();
- assert context != null;
- // This is how the fragment looks at first. Since the transition is one-way, we don't need to make
- // this a Scene.
- View item = LayoutInflater.from(context).inflate(R.layout.item_meat_grid, root, false);
- assert item != null;
- bind(item);
- // We adjust the position of the initial image with LayoutParams using the values supplied
- // as the fragment arguments.
- Bundle args = getArguments();
- FrameLayout.LayoutParams params = null;
- if (args != null) {
- params = new FrameLayout.LayoutParams(
- args.getInt(ARG_WIDTH), args.getInt(ARG_HEIGHT));
- params.topMargin = args.getInt(ARG_Y);
- params.leftMargin = args.getInt(ARG_X);
- }
- root.addView(item, params);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- }
-
- /**
- * Bind the views inside of parent with the fragment arguments.
- *
- * @param parent The parent of views to bind.
- */
- private void bind(View parent) {
- Bundle args = getArguments();
- if (args == null) {
- return;
- }
- ImageView image = (ImageView) parent.findViewById(R.id.image);
- image.setImageResource(args.getInt(ARG_RESOURCE_ID));
- TextView title = (TextView) parent.findViewById(R.id.title);
- title.setText(args.getString(ARG_TITLE));
- }
-
- @Override
- public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
- Animation animation = AnimationUtils.loadAnimation(getActivity(),
- enter ? android.R.anim.fade_in : android.R.anim.fade_out);
- // We bind a listener for the fragment transaction. We only bind it when
- // this fragment is entering.
- if (animation != null && enter) {
- animation.setAnimationListener(this);
- }
- return animation;
- }
-
- @Override
- public void onAnimationStart(Animation animation) {
- // This method is called at the end of the animation for the fragment transaction.
- // There is nothing we need to do in this sample.
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- // This method is called at the end of the animation for the fragment transaction,
- // which is perfect time to start our Transition.
- Log.i(TAG, "Fragment animation ended. Starting a Transition.");
- final Scene scene = Scene.getSceneForLayout((ViewGroup) getView(),
- R.layout.fragment_detail_content, getActivity());
- TransitionManager.go(scene);
- // Note that we need to bind views with data after we call TransitionManager.go().
- bind(scene.getSceneRoot());
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- // This method is never called in this sample because the animation doesn't repeat.
- }
-
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java
deleted file mode 100644
index c072eb9..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/FragmentTransitionFragment.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2014 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.fragmenttransition;
-
-import com.example.android.common.logger.Log;
-
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.AdapterView;
-import android.widget.GridView;
-
-public class FragmentTransitionFragment extends Fragment implements AdapterView.OnItemClickListener {
-
- private static final String TAG = "FragmentTransitionFragment";
-
- private MeatAdapter mAdapter;
-
- public static FragmentTransitionFragment newInstance() {
- return new FragmentTransitionFragment();
- }
-
- public FragmentTransitionFragment() {
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- // This is the adapter we use to populate the grid.
- mAdapter = new MeatAdapter(inflater, R.layout.item_meat_grid);
- // Inflate the layout with a GridView in it.
- return inflater.inflate(R.layout.fragment_fragment_transition, container, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- GridView grid = (GridView) view.findViewById(R.id.grid);
- grid.setAdapter(mAdapter);
- grid.setOnItemClickListener(this);
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Meat meat = mAdapter.getItem(position);
- Log.i(TAG, meat.title + " clicked. Replacing fragment.");
- // We start the fragment transaction here. It is just an ordinary fragment transaction.
- getActivity().getSupportFragmentManager()
- .beginTransaction()
- .replace(R.id.sample_content_fragment,
- DetailFragment.newInstance(meat.resourceId, meat.title,
- (int) view.getX(), (int) view.getY(),
- view.getWidth(), view.getHeight())
- )
- // We push the fragment transaction to back stack. User can go back to the
- // previous fragment by pressing back button.
- .addToBackStack("detail")
- .commit();
- }
-
- @Override
- public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
- return AnimationUtils.loadAnimation(getActivity(),
- enter ? android.R.anim.fade_in : android.R.anim.fade_out);
- }
-
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java
deleted file mode 100644
index fa019be..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MainActivity.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-* 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.fragmenttransition;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentTransaction;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.ViewAnimator;
-
-import com.example.android.common.activities.SampleActivityBase;
-import com.example.android.common.logger.Log;
-import com.example.android.common.logger.LogFragment;
-import com.example.android.common.logger.LogWrapper;
-import com.example.android.common.logger.MessageOnlyLogFilter;
-
-/**
- * A simple launcher activity containing a summary sample description, sample log and a custom
- * {@link android.support.v4.app.Fragment} which can display a view.
- * <p>
- * For devices with displays with a width of 720dp or greater, the sample log is always visible,
- * on other devices it's visibility is controlled by an item on the Action Bar.
- */
-public class MainActivity extends SampleActivityBase {
-
- public static final String TAG = "MainActivity";
-
- // Whether the Log Fragment is currently shown
- private boolean mLogShown;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- if (savedInstanceState == null) {
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- FragmentTransitionFragment fragment = new FragmentTransitionFragment();
- transaction.replace(R.id.sample_content_fragment, fragment);
- transaction.commit();
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);
- logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);
- logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);
-
- return super.onPrepareOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch(item.getItemId()) {
- case R.id.menu_toggle_log:
- mLogShown = !mLogShown;
- ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);
- if (mLogShown) {
- output.setDisplayedChild(1);
- } else {
- output.setDisplayedChild(0);
- }
- supportInvalidateOptionsMenu();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- /** Create a chain of targets that will receive log data */
- @Override
- public void initializeLogging() {
- // Wraps Android's native log framework.
- LogWrapper logWrapper = new LogWrapper();
- // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
- Log.setLogNode(logWrapper);
-
- // Filter strips out everything except the message text.
- MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();
- logWrapper.setNext(msgFilter);
-
- // On screen logging via a fragment with a TextView.
- LogFragment logFragment = (LogFragment) getSupportFragmentManager()
- .findFragmentById(R.id.log_fragment);
- msgFilter.setNext(logFragment.getLogView());
-
- Log.i(TAG, "Ready");
- }
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java
deleted file mode 100644
index 2f2fdfa..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/Meat.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2014 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.fragmenttransition;
-
-/**
- * This represents a sample data.
- */
-public class Meat {
-
- public int resourceId;
- public String title;
-
- public Meat(int resourceId, String title) {
- this.resourceId = resourceId;
- this.title = title;
- }
-
- public static final Meat[] MEATS = {
- new Meat(R.drawable.p1, "First"),
- new Meat(R.drawable.p2, "Second"),
- new Meat(R.drawable.p3, "Third"),
- new Meat(R.drawable.p4, "Fourth"),
- new Meat(R.drawable.p5, "Fifth"),
- new Meat(R.drawable.p6, "Sixth"),
- new Meat(R.drawable.p7, "Seventh"),
- new Meat(R.drawable.p8, "Eighth"),
- new Meat(R.drawable.p9, "Ninth"),
- new Meat(R.drawable.p10, "Tenth"),
- new Meat(R.drawable.p11, "Eleventh"),
- };
-
-}
diff --git a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java b/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java
deleted file mode 100644
index 307fd85..0000000
--- a/samples/browseable/FragmentTransition/src/com.example.android.fragmenttransition/MeatAdapter.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2014 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.fragmenttransition;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-class MeatAdapter extends BaseAdapter {
-
- private final LayoutInflater mLayoutInflater;
- private final int mResourceId;
-
- public MeatAdapter(LayoutInflater inflater, int resourceId) {
- mLayoutInflater = inflater;
- mResourceId = resourceId;
- }
-
- @Override
- public int getCount() {
- return Meat.MEATS.length;
- }
-
- @Override
- public Meat getItem(int position) {
- return Meat.MEATS[position];
- }
-
- @Override
- public long getItemId(int position) {
- return Meat.MEATS[position].resourceId;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- final View view;
- final ViewHolder holder;
- if (null == convertView) {
- view = mLayoutInflater.inflate(mResourceId, parent, false);
- holder = new ViewHolder();
- assert view != null;
- holder.image = (ImageView) view.findViewById(R.id.image);
- holder.title = (TextView) view.findViewById(R.id.title);
- view.setTag(holder);
- } else {
- view = convertView;
- holder = (ViewHolder) view.getTag();
- }
- bindView(holder, position);
- return view;
- }
-
- public void bindView(ViewHolder holder, int position) {
- Meat meat = getItem(position);
- holder.image.setImageResource(meat.resourceId);
- holder.title.setText(meat.title);
- }
-
- public static class ViewHolder {
- public ImageView image;
- public TextView title;
- }
-
-}
diff --git a/samples/browseable/Geofencing/Application/res/layout/activity_main.xml b/samples/browseable/Geofencing/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/Geofencing/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/GeofenceTransitionsIntentService.java b/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/GeofenceTransitionsIntentService.java
index 8ae0cbc..53117e3 100644
--- a/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/GeofenceTransitionsIntentService.java
+++ b/samples/browseable/Geofencing/Application/src/com.example.android.wearable.geofencing/GeofenceTransitionsIntentService.java
@@ -23,8 +23,11 @@
import static com.example.android.wearable.geofencing.Constants.TAG;
import android.app.IntentService;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
@@ -100,13 +103,25 @@
// Delete the data item when leaving a geofence region.
mGoogleApiClient.blockingConnect(CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
Wearable.DataApi.deleteDataItems(mGoogleApiClient, GEOFENCE_DATA_ITEM_URI).await();
- Toast.makeText(this, getString(R.string.exiting_geofence),
- Toast.LENGTH_SHORT).show();
+ showToast(this, R.string.exiting_geofence);
mGoogleApiClient.disconnect();
}
}
}
+ /**
+ * Showing a toast message, using the Main thread
+ */
+ private void showToast(final Context context, final int resourceId) {
+ Handler mainThread = new Handler(Looper.getMainLooper());
+ mainThread.post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(context, context.getString(resourceId), Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
@Override
public void onConnected(Bundle connectionHint) {
}
diff --git a/samples/browseable/GridViewPager/Wearable/AndroidManifest.xml b/samples/browseable/GridViewPager/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/AndroidManifest.xml
rename to samples/browseable/GridViewPager/AndroidManifest.xml
diff --git a/samples/browseable/GridViewPager/Application/res/drawable-hdpi/ic_launcher.png b/samples/browseable/GridViewPager/Application/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/GridViewPager/Application/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/GridViewPager/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/GridViewPager/Application/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/GridViewPager/Application/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/GridViewPager/Application/res/drawable-mdpi/ic_launcher.png b/samples/browseable/GridViewPager/Application/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/GridViewPager/Application/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/GridViewPager/Application/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/GridViewPager/Application/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/GridViewPager/Application/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/GridViewPager/Application/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/GridViewPager/Application/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/GridViewPager/Application/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/GridViewPager/Application/res/layout/activity_main.xml b/samples/browseable/GridViewPager/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/GridViewPager/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/GridViewPager/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/GridViewPager/Application/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2..0000000
--- a/samples/browseable/GridViewPager/Application/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- 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/GridViewPager/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/GridViewPager/Application/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/GridViewPager/Application/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/GridViewPager/Application/res/values-v11/template-styles.xml b/samples/browseable/GridViewPager/Application/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66..0000000
--- a/samples/browseable/GridViewPager/Application/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- 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/GridViewPager/Application/res/values-v21/base-colors.xml b/samples/browseable/GridViewPager/Application/res/values-v21/base-colors.xml
deleted file mode 100644
index 34c9cd1..0000000
--- a/samples/browseable/GridViewPager/Application/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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>
-
-
-</resources>
diff --git a/samples/browseable/GridViewPager/Application/res/values-v21/base-template-styles.xml b/samples/browseable/GridViewPager/Application/res/values-v21/base-template-styles.xml
deleted file mode 100644
index 0b2948f..0000000
--- a/samples/browseable/GridViewPager/Application/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
- </style>
-
-</resources>
diff --git a/samples/browseable/GridViewPager/Application/res/values/base-strings.xml b/samples/browseable/GridViewPager/Application/res/values/base-strings.xml
deleted file mode 100644
index c5f9122..0000000
--- a/samples/browseable/GridViewPager/Application/res/values/base-strings.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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">GridViewPager</string>
- <string name="intro_message">
- <![CDATA[
-
-
- Demonstrates how to implement a GridViewPager in your wearable app.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/GridViewPager/Application/res/values/template-dimens.xml b/samples/browseable/GridViewPager/Application/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/GridViewPager/Application/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/GridViewPager/Application/res/values/template-styles.xml b/samples/browseable/GridViewPager/Application/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593..0000000
--- a/samples/browseable/GridViewPager/Application/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- 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/GridViewPager/Wearable/res/drawable-hdpi/bugdroid.png b/samples/browseable/GridViewPager/res/drawable-hdpi/bugdroid.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/bugdroid.png
rename to samples/browseable/GridViewPager/res/drawable-hdpi/bugdroid.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_launcher.png b/samples/browseable/GridViewPager/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/GridViewPager/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_swipe_arrow_left.png b/samples/browseable/GridViewPager/res/drawable-hdpi/ic_swipe_arrow_left.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_swipe_arrow_left.png
rename to samples/browseable/GridViewPager/res/drawable-hdpi/ic_swipe_arrow_left.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_swipe_arrow_right.png b/samples/browseable/GridViewPager/res/drawable-hdpi/ic_swipe_arrow_right.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_swipe_arrow_right.png
rename to samples/browseable/GridViewPager/res/drawable-hdpi/ic_swipe_arrow_right.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_swipe_arrow_up.png b/samples/browseable/GridViewPager/res/drawable-hdpi/ic_swipe_arrow_up.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-hdpi/ic_swipe_arrow_up.png
rename to samples/browseable/GridViewPager/res/drawable-hdpi/ic_swipe_arrow_up.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/bugdroid.png b/samples/browseable/GridViewPager/res/drawable-mdpi/bugdroid.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/bugdroid.png
rename to samples/browseable/GridViewPager/res/drawable-mdpi/bugdroid.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_launcher.png b/samples/browseable/GridViewPager/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/GridViewPager/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_swipe_arrow_left.png b/samples/browseable/GridViewPager/res/drawable-mdpi/ic_swipe_arrow_left.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_swipe_arrow_left.png
rename to samples/browseable/GridViewPager/res/drawable-mdpi/ic_swipe_arrow_left.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_swipe_arrow_right.png b/samples/browseable/GridViewPager/res/drawable-mdpi/ic_swipe_arrow_right.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_swipe_arrow_right.png
rename to samples/browseable/GridViewPager/res/drawable-mdpi/ic_swipe_arrow_right.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_swipe_arrow_up.png b/samples/browseable/GridViewPager/res/drawable-mdpi/ic_swipe_arrow_up.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-mdpi/ic_swipe_arrow_up.png
rename to samples/browseable/GridViewPager/res/drawable-mdpi/ic_swipe_arrow_up.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-nodpi/bugdroid_large.png b/samples/browseable/GridViewPager/res/drawable-nodpi/bugdroid_large.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-nodpi/bugdroid_large.png
rename to samples/browseable/GridViewPager/res/drawable-nodpi/bugdroid_large.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/GridViewPager/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/GridViewPager/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_swipe_arrow_left.png b/samples/browseable/GridViewPager/res/drawable-xhdpi/ic_swipe_arrow_left.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_swipe_arrow_left.png
rename to samples/browseable/GridViewPager/res/drawable-xhdpi/ic_swipe_arrow_left.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_swipe_arrow_right.png b/samples/browseable/GridViewPager/res/drawable-xhdpi/ic_swipe_arrow_right.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_swipe_arrow_right.png
rename to samples/browseable/GridViewPager/res/drawable-xhdpi/ic_swipe_arrow_right.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_swipe_arrow_up.png b/samples/browseable/GridViewPager/res/drawable-xhdpi/ic_swipe_arrow_up.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-xhdpi/ic_swipe_arrow_up.png
rename to samples/browseable/GridViewPager/res/drawable-xhdpi/ic_swipe_arrow_up.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/GridViewPager/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/GridViewPager/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_1.png b/samples/browseable/GridViewPager/res/drawable/debug_background_1.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_1.png
rename to samples/browseable/GridViewPager/res/drawable/debug_background_1.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_2.png b/samples/browseable/GridViewPager/res/drawable/debug_background_2.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_2.png
rename to samples/browseable/GridViewPager/res/drawable/debug_background_2.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_3.png b/samples/browseable/GridViewPager/res/drawable/debug_background_3.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_3.png
rename to samples/browseable/GridViewPager/res/drawable/debug_background_3.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_4.png b/samples/browseable/GridViewPager/res/drawable/debug_background_4.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_4.png
rename to samples/browseable/GridViewPager/res/drawable/debug_background_4.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_5.png b/samples/browseable/GridViewPager/res/drawable/debug_background_5.png
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable/debug_background_5.png
rename to samples/browseable/GridViewPager/res/drawable/debug_background_5.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable/gradient.xml b/samples/browseable/GridViewPager/res/drawable/gradient.xml
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable/gradient.xml
rename to samples/browseable/GridViewPager/res/drawable/gradient.xml
diff --git a/samples/browseable/GridViewPager/Wearable/res/drawable/shape.xml b/samples/browseable/GridViewPager/res/drawable/shape.xml
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/drawable/shape.xml
rename to samples/browseable/GridViewPager/res/drawable/shape.xml
diff --git a/samples/browseable/GridViewPager/Wearable/res/layout/activity_main.xml b/samples/browseable/GridViewPager/res/layout/activity_main.xml
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/layout/activity_main.xml
rename to samples/browseable/GridViewPager/res/layout/activity_main.xml
diff --git a/samples/browseable/GridViewPager/Wearable/res/layout/custom_fragment.xml b/samples/browseable/GridViewPager/res/layout/custom_fragment.xml
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/layout/custom_fragment.xml
rename to samples/browseable/GridViewPager/res/layout/custom_fragment.xml
diff --git a/samples/browseable/GridViewPager/Wearable/res/values/dimens.xml b/samples/browseable/GridViewPager/res/values/dimens.xml
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/values/dimens.xml
rename to samples/browseable/GridViewPager/res/values/dimens.xml
diff --git a/samples/browseable/GridViewPager/Wearable/res/values/strings.xml b/samples/browseable/GridViewPager/res/values/strings.xml
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/res/values/strings.xml
rename to samples/browseable/GridViewPager/res/values/strings.xml
diff --git a/samples/browseable/GridViewPager/Wearable/src/com.example.android.wearable.gridviewpager/CustomFragment.java b/samples/browseable/GridViewPager/src/com.example.android.wearable.gridviewpager/CustomFragment.java
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/src/com.example.android.wearable.gridviewpager/CustomFragment.java
rename to samples/browseable/GridViewPager/src/com.example.android.wearable.gridviewpager/CustomFragment.java
diff --git a/samples/browseable/GridViewPager/Wearable/src/com.example.android.wearable.gridviewpager/MainActivity.java b/samples/browseable/GridViewPager/src/com.example.android.wearable.gridviewpager/MainActivity.java
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/src/com.example.android.wearable.gridviewpager/MainActivity.java
rename to samples/browseable/GridViewPager/src/com.example.android.wearable.gridviewpager/MainActivity.java
diff --git a/samples/browseable/GridViewPager/Wearable/src/com.example.android.wearable.gridviewpager/SampleGridPagerAdapter.java b/samples/browseable/GridViewPager/src/com.example.android.wearable.gridviewpager/SampleGridPagerAdapter.java
similarity index 100%
rename from samples/browseable/GridViewPager/Wearable/src/com.example.android.wearable.gridviewpager/SampleGridPagerAdapter.java
rename to samples/browseable/GridViewPager/src/com.example.android.wearable.gridviewpager/SampleGridPagerAdapter.java
diff --git a/samples/browseable/HdrViewfinder/res/layout/activity_main.xml b/samples/browseable/HdrViewfinder/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/HdrViewfinder/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/HorizontalPaging/res/layout/activity_main.xml b/samples/browseable/HorizontalPaging/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/HorizontalPaging/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/JobScheduler/res/layout/activity_main.xml b/samples/browseable/JobScheduler/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/JobScheduler/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/JumpingJack/Wearable/AndroidManifest.xml b/samples/browseable/JumpingJack/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/AndroidManifest.xml
rename to samples/browseable/JumpingJack/AndroidManifest.xml
diff --git a/samples/browseable/JumpingJack/Application/AndroidManifest.xml b/samples/browseable/JumpingJack/Application/AndroidManifest.xml
deleted file mode 100644
index 8605fb0..0000000
--- a/samples/browseable/JumpingJack/Application/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2014 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.jumpingjack">
-
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="21" />
- <uses-permission android:name="android.permission.VIBRATE"/>
-
- <application android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
- </application>
-
-</manifest>
-
diff --git a/samples/browseable/JumpingJack/Application/res/drawable-hdpi/ic_launcher.png b/samples/browseable/JumpingJack/Application/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/JumpingJack/Application/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/JumpingJack/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/JumpingJack/Application/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/JumpingJack/Application/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/JumpingJack/Application/res/drawable-mdpi/ic_launcher.png b/samples/browseable/JumpingJack/Application/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/JumpingJack/Application/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/JumpingJack/Application/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/JumpingJack/Application/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/JumpingJack/Application/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/JumpingJack/Application/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/JumpingJack/Application/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index ab80bcd..0000000
--- a/samples/browseable/JumpingJack/Application/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/JumpingJack/Application/res/layout/activity_main.xml b/samples/browseable/JumpingJack/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/JumpingJack/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/JumpingJack/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/JumpingJack/Application/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2..0000000
--- a/samples/browseable/JumpingJack/Application/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- 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/JumpingJack/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/JumpingJack/Application/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/JumpingJack/Application/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/JumpingJack/Application/res/values-v11/template-styles.xml b/samples/browseable/JumpingJack/Application/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66..0000000
--- a/samples/browseable/JumpingJack/Application/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- 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/JumpingJack/Application/res/values-v21/base-colors.xml b/samples/browseable/JumpingJack/Application/res/values-v21/base-colors.xml
deleted file mode 100644
index 34c9cd1..0000000
--- a/samples/browseable/JumpingJack/Application/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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>
-
-
-</resources>
diff --git a/samples/browseable/JumpingJack/Application/res/values-v21/base-template-styles.xml b/samples/browseable/JumpingJack/Application/res/values-v21/base-template-styles.xml
deleted file mode 100644
index 0b2948f..0000000
--- a/samples/browseable/JumpingJack/Application/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
- </style>
-
-</resources>
diff --git a/samples/browseable/JumpingJack/Application/res/values/base-strings.xml b/samples/browseable/JumpingJack/Application/res/values/base-strings.xml
deleted file mode 100644
index 7553d3e..0000000
--- a/samples/browseable/JumpingJack/Application/res/values/base-strings.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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">JumpingJack</string>
- <string name="intro_message">
- <![CDATA[
-
-
- Uses the Gravity sensor to count how many jumping jacks you have performed.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/JumpingJack/Application/res/values/template-dimens.xml b/samples/browseable/JumpingJack/Application/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/JumpingJack/Application/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/JumpingJack/Application/res/values/template-styles.xml b/samples/browseable/JumpingJack/Application/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593..0000000
--- a/samples/browseable/JumpingJack/Application/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- 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/JumpingJack/Application/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/JumpingJack/Application/src/com.example.android.common/activities/SampleActivityBase.java
deleted file mode 100644
index 3228927..0000000
--- a/samples/browseable/JumpingJack/Application/src/com.example.android.common/activities/SampleActivityBase.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-* 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.common.activities;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-
-import com.example.android.common.logger.Log;
-import com.example.android.common.logger.LogWrapper;
-
-/**
- * Base launcher activity, to handle most of the common plumbing for samples.
- */
-public class SampleActivityBase extends FragmentActivity {
-
- public static final String TAG = "SampleActivityBase";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- initializeLogging();
- }
-
- /** Set up targets to receive log data */
- public void initializeLogging() {
- // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
- // Wraps Android's native log framework
- LogWrapper logWrapper = new LogWrapper();
- Log.setLogNode(logWrapper);
-
- Log.i(TAG, "Ready");
- }
-}
diff --git a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/Log.java b/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/Log.java
deleted file mode 100644
index 17503c5..0000000
--- a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/Log.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-/**
- * Helper class for a list (or tree) of LoggerNodes.
- *
- * <p>When this is set as the head of the list,
- * an instance of it can function as a drop-in replacement for {@link android.util.Log}.
- * Most of the methods in this class server only to map a method call in Log to its equivalent
- * in LogNode.</p>
- */
-public class Log {
- // Grabbing the native values from Android's native logging facilities,
- // to make for easy migration and interop.
- public static final int NONE = -1;
- public static final int VERBOSE = android.util.Log.VERBOSE;
- public static final int DEBUG = android.util.Log.DEBUG;
- public static final int INFO = android.util.Log.INFO;
- public static final int WARN = android.util.Log.WARN;
- public static final int ERROR = android.util.Log.ERROR;
- public static final int ASSERT = android.util.Log.ASSERT;
-
- // Stores the beginning of the LogNode topology.
- private static LogNode mLogNode;
-
- /**
- * Returns the next LogNode in the linked list.
- */
- public static LogNode getLogNode() {
- return mLogNode;
- }
-
- /**
- * Sets the LogNode data will be sent to.
- */
- public static void setLogNode(LogNode node) {
- mLogNode = node;
- }
-
- /**
- * Instructs the LogNode to print the log data provided. Other LogNodes can
- * be chained to the end of the LogNode as desired.
- *
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void println(int priority, String tag, String msg, Throwable tr) {
- if (mLogNode != null) {
- mLogNode.println(priority, tag, msg, tr);
- }
- }
-
- /**
- * Instructs the LogNode to print the log data provided. Other LogNodes can
- * be chained to the end of the LogNode as desired.
- *
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- */
- public static void println(int priority, String tag, String msg) {
- println(priority, tag, msg, null);
- }
-
- /**
- * Prints a message at VERBOSE priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void v(String tag, String msg, Throwable tr) {
- println(VERBOSE, tag, msg, tr);
- }
-
- /**
- * Prints a message at VERBOSE priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void v(String tag, String msg) {
- v(tag, msg, null);
- }
-
-
- /**
- * Prints a message at DEBUG priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void d(String tag, String msg, Throwable tr) {
- println(DEBUG, tag, msg, tr);
- }
-
- /**
- * Prints a message at DEBUG priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void d(String tag, String msg) {
- d(tag, msg, null);
- }
-
- /**
- * Prints a message at INFO priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void i(String tag, String msg, Throwable tr) {
- println(INFO, tag, msg, tr);
- }
-
- /**
- * Prints a message at INFO priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void i(String tag, String msg) {
- i(tag, msg, null);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void w(String tag, String msg, Throwable tr) {
- println(WARN, tag, msg, tr);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void w(String tag, String msg) {
- w(tag, msg, null);
- }
-
- /**
- * Prints a message at WARN priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void w(String tag, Throwable tr) {
- w(tag, null, tr);
- }
-
- /**
- * Prints a message at ERROR priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void e(String tag, String msg, Throwable tr) {
- println(ERROR, tag, msg, tr);
- }
-
- /**
- * Prints a message at ERROR priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void e(String tag, String msg) {
- e(tag, msg, null);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void wtf(String tag, String msg, Throwable tr) {
- println(ASSERT, tag, msg, tr);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged.
- */
- public static void wtf(String tag, String msg) {
- wtf(tag, msg, null);
- }
-
- /**
- * Prints a message at ASSERT priority.
- *
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public static void wtf(String tag, Throwable tr) {
- wtf(tag, null, tr);
- }
-}
diff --git a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogFragment.java
deleted file mode 100644
index b302acd..0000000
--- a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogFragment.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-* 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.
-*/
-/*
- * 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.common.logger;
-
-import android.graphics.Typeface;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ScrollView;
-
-/**
- * Simple fraggment which contains a LogView and uses is to output log data it receives
- * through the LogNode interface.
- */
-public class LogFragment extends Fragment {
-
- private LogView mLogView;
- private ScrollView mScrollView;
-
- public LogFragment() {}
-
- public View inflateViews() {
- mScrollView = new ScrollView(getActivity());
- ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- mScrollView.setLayoutParams(scrollParams);
-
- mLogView = new LogView(getActivity());
- ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams);
- logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
- mLogView.setLayoutParams(logParams);
- mLogView.setClickable(true);
- mLogView.setFocusable(true);
- mLogView.setTypeface(Typeface.MONOSPACE);
-
- // Want to set padding as 16 dips, setPadding takes pixels. Hooray math!
- int paddingDips = 16;
- double scale = getResources().getDisplayMetrics().density;
- int paddingPixels = (int) ((paddingDips * (scale)) + .5);
- mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels);
- mLogView.setCompoundDrawablePadding(paddingPixels);
-
- mLogView.setGravity(Gravity.BOTTOM);
- mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium);
-
- mScrollView.addView(mLogView);
- return mScrollView;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
-
- View result = inflateViews();
-
- mLogView.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
- @Override
- public void afterTextChanged(Editable s) {
- mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
- }
- });
- return result;
- }
-
- public LogView getLogView() {
- return mLogView;
- }
-}
\ No newline at end of file
diff --git a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogNode.java b/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogNode.java
deleted file mode 100644
index bc37cab..0000000
--- a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogNode.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012 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.common.logger;
-
-/**
- * Basic interface for a logging system that can output to one or more targets.
- * Note that in addition to classes that will output these logs in some format,
- * one can also implement this interface over a filter and insert that in the chain,
- * such that no targets further down see certain data, or see manipulated forms of the data.
- * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data
- * it received to HTML and sent it along to the next node in the chain, without printing it
- * anywhere.
- */
-public interface LogNode {
-
- /**
- * Instructs first LogNode in the list to print the log data provided.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- public void println(int priority, String tag, String msg, Throwable tr);
-
-}
diff --git a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogView.java b/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogView.java
deleted file mode 100644
index c01542b..0000000
--- a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogView.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-import android.app.Activity;
-import android.content.Context;
-import android.util.*;
-import android.widget.TextView;
-
-/** Simple TextView which is used to output log data received through the LogNode interface.
-*/
-public class LogView extends TextView implements LogNode {
-
- public LogView(Context context) {
- super(context);
- }
-
- public LogView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public LogView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- /**
- * Formats the log data and prints it out to the LogView.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
-
-
- String priorityStr = null;
-
- // For the purposes of this View, we want to print the priority as readable text.
- switch(priority) {
- case android.util.Log.VERBOSE:
- priorityStr = "VERBOSE";
- break;
- case android.util.Log.DEBUG:
- priorityStr = "DEBUG";
- break;
- case android.util.Log.INFO:
- priorityStr = "INFO";
- break;
- case android.util.Log.WARN:
- priorityStr = "WARN";
- break;
- case android.util.Log.ERROR:
- priorityStr = "ERROR";
- break;
- case android.util.Log.ASSERT:
- priorityStr = "ASSERT";
- break;
- default:
- break;
- }
-
- // Handily, the Log class has a facility for converting a stack trace into a usable string.
- String exceptionStr = null;
- if (tr != null) {
- exceptionStr = android.util.Log.getStackTraceString(tr);
- }
-
- // Take the priority, tag, message, and exception, and concatenate as necessary
- // into one usable line of text.
- final StringBuilder outputBuilder = new StringBuilder();
-
- String delimiter = "\t";
- appendIfNotNull(outputBuilder, priorityStr, delimiter);
- appendIfNotNull(outputBuilder, tag, delimiter);
- appendIfNotNull(outputBuilder, msg, delimiter);
- appendIfNotNull(outputBuilder, exceptionStr, delimiter);
-
- // In case this was originally called from an AsyncTask or some other off-UI thread,
- // make sure the update occurs within the UI thread.
- ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() {
- @Override
- public void run() {
- // Display the text we just generated within the LogView.
- appendToLog(outputBuilder.toString());
- }
- })));
-
- if (mNext != null) {
- mNext.println(priority, tag, msg, tr);
- }
- }
-
- public LogNode getNext() {
- return mNext;
- }
-
- public void setNext(LogNode node) {
- mNext = node;
- }
-
- /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since
- * the logger takes so many arguments that might be null, this method helps cut out some of the
- * agonizing tedium of writing the same 3 lines over and over.
- * @param source StringBuilder containing the text to append to.
- * @param addStr The String to append
- * @param delimiter The String to separate the source and appended strings. A tab or comma,
- * for instance.
- * @return The fully concatenated String as a StringBuilder
- */
- private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) {
- if (addStr != null) {
- if (addStr.length() == 0) {
- delimiter = "";
- }
-
- return source.append(addStr).append(delimiter);
- }
- return source;
- }
-
- // The next LogNode in the chain.
- LogNode mNext;
-
- /** Outputs the string as a new line of log data in the LogView. */
- public void appendToLog(String s) {
- append("\n" + s);
- }
-
-
-}
diff --git a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogWrapper.java
deleted file mode 100644
index 16a9e7b..0000000
--- a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/LogWrapper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2012 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.common.logger;
-
-import android.util.Log;
-
-/**
- * Helper class which wraps Android's native Log utility in the Logger interface. This way
- * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously.
- */
-public class LogWrapper implements LogNode {
-
- // For piping: The next node to receive Log data after this one has done its work.
- private LogNode mNext;
-
- /**
- * Returns the next LogNode in the linked list.
- */
- public LogNode getNext() {
- return mNext;
- }
-
- /**
- * Sets the LogNode data will be sent to..
- */
- public void setNext(LogNode node) {
- mNext = node;
- }
-
- /**
- * Prints data out to the console using Android's native log mechanism.
- * @param priority Log level of the data being logged. Verbose, Error, etc.
- * @param tag Tag for for the log data. Can be used to organize log statements.
- * @param msg The actual message to be logged. The actual message to be logged.
- * @param tr If an exception was thrown, this can be sent along for the logging facilities
- * to extract and print useful information.
- */
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
- // There actually are log methods that don't take a msg parameter. For now,
- // if that's the case, just convert null to the empty string and move on.
- String useMsg = msg;
- if (useMsg == null) {
- useMsg = "";
- }
-
- // If an exeption was provided, convert that exception to a usable string and attach
- // it to the end of the msg method.
- if (tr != null) {
- msg += "\n" + Log.getStackTraceString(tr);
- }
-
- // This is functionally identical to Log.x(tag, useMsg);
- // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg)
- Log.println(priority, tag, useMsg);
-
- // If this isn't the last node in the chain, move things along.
- if (mNext != null) {
- mNext.println(priority, tag, msg, tr);
- }
- }
-}
diff --git a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/MessageOnlyLogFilter.java
deleted file mode 100644
index 19967dc..0000000
--- a/samples/browseable/JumpingJack/Application/src/com.example.android.common/logger/MessageOnlyLogFilter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.
- */
-package com.example.android.common.logger;
-
-/**
- * Simple {@link LogNode} filter, removes everything except the message.
- * Useful for situations like on-screen log output where you don't want a lot of metadata displayed,
- * just easy-to-read message updates as they're happening.
- */
-public class MessageOnlyLogFilter implements LogNode {
-
- LogNode mNext;
-
- /**
- * Takes the "next" LogNode as a parameter, to simplify chaining.
- *
- * @param next The next LogNode in the pipeline.
- */
- public MessageOnlyLogFilter(LogNode next) {
- mNext = next;
- }
-
- public MessageOnlyLogFilter() {
- }
-
- @Override
- public void println(int priority, String tag, String msg, Throwable tr) {
- if (mNext != null) {
- getNext().println(Log.NONE, null, msg, null);
- }
- }
-
- /**
- * Returns the next LogNode in the chain.
- */
- public LogNode getNext() {
- return mNext;
- }
-
- /**
- * Sets the LogNode data will be sent to..
- */
- public void setNext(LogNode node) {
- mNext = node;
- }
-
-}
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable-hdpi/btn_reset_normal_200.png b/samples/browseable/JumpingJack/res/drawable-hdpi/btn_reset_normal_200.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable-hdpi/btn_reset_normal_200.png
rename to samples/browseable/JumpingJack/res/drawable-hdpi/btn_reset_normal_200.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable-hdpi/btn_reset_pressed_200.png b/samples/browseable/JumpingJack/res/drawable-hdpi/btn_reset_pressed_200.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable-hdpi/btn_reset_pressed_200.png
rename to samples/browseable/JumpingJack/res/drawable-hdpi/btn_reset_pressed_200.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable-hdpi/ic_launcher.png b/samples/browseable/JumpingJack/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/JumpingJack/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable-mdpi/ic_launcher.png b/samples/browseable/JumpingJack/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/JumpingJack/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/JumpingJack/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/JumpingJack/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/JumpingJack/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/JumpingJack/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable-xxxhdpi/ic_launcher.png b/samples/browseable/JumpingJack/res/drawable-xxxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable-xxxhdpi/ic_launcher.png
rename to samples/browseable/JumpingJack/res/drawable-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable/empty_10.png b/samples/browseable/JumpingJack/res/drawable/empty_10.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable/empty_10.png
rename to samples/browseable/JumpingJack/res/drawable/empty_10.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable/full_10.png b/samples/browseable/JumpingJack/res/drawable/full_10.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable/full_10.png
rename to samples/browseable/JumpingJack/res/drawable/full_10.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable/jump_down_50.png b/samples/browseable/JumpingJack/res/drawable/jump_down_50.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable/jump_down_50.png
rename to samples/browseable/JumpingJack/res/drawable/jump_down_50.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable/jump_up_50.png b/samples/browseable/JumpingJack/res/drawable/jump_up_50.png
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable/jump_up_50.png
rename to samples/browseable/JumpingJack/res/drawable/jump_up_50.png
Binary files differ
diff --git a/samples/browseable/JumpingJack/Wearable/res/drawable/submit_button.xml b/samples/browseable/JumpingJack/res/drawable/submit_button.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/drawable/submit_button.xml
rename to samples/browseable/JumpingJack/res/drawable/submit_button.xml
diff --git a/samples/browseable/JumpingJack/Wearable/res/layout/counter_layout.xml b/samples/browseable/JumpingJack/res/layout/counter_layout.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/layout/counter_layout.xml
rename to samples/browseable/JumpingJack/res/layout/counter_layout.xml
diff --git a/samples/browseable/JumpingJack/Wearable/res/layout/jj_layout.xml b/samples/browseable/JumpingJack/res/layout/jj_layout.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/layout/jj_layout.xml
rename to samples/browseable/JumpingJack/res/layout/jj_layout.xml
diff --git a/samples/browseable/JumpingJack/Wearable/res/layout/setting_layout.xml b/samples/browseable/JumpingJack/res/layout/setting_layout.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/layout/setting_layout.xml
rename to samples/browseable/JumpingJack/res/layout/setting_layout.xml
diff --git a/samples/browseable/JumpingJack/Wearable/res/values/colors.xml b/samples/browseable/JumpingJack/res/values/colors.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/values/colors.xml
rename to samples/browseable/JumpingJack/res/values/colors.xml
diff --git a/samples/browseable/JumpingJack/Wearable/res/values/dimens.xml b/samples/browseable/JumpingJack/res/values/dimens.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/values/dimens.xml
rename to samples/browseable/JumpingJack/res/values/dimens.xml
diff --git a/samples/browseable/JumpingJack/Wearable/res/values/strings.xml b/samples/browseable/JumpingJack/res/values/strings.xml
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/res/values/strings.xml
rename to samples/browseable/JumpingJack/res/values/strings.xml
diff --git a/samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/MainActivity.java b/samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/MainActivity.java
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/MainActivity.java
rename to samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/MainActivity.java
diff --git a/samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/PagerAdapter.java b/samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/PagerAdapter.java
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/PagerAdapter.java
rename to samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/PagerAdapter.java
diff --git a/samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/Utils.java b/samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/Utils.java
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/Utils.java
rename to samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/Utils.java
diff --git a/samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/fragments/CounterFragment.java b/samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/fragments/CounterFragment.java
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/fragments/CounterFragment.java
rename to samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/fragments/CounterFragment.java
diff --git a/samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/fragments/SettingsFragment.java b/samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/fragments/SettingsFragment.java
similarity index 100%
rename from samples/browseable/JumpingJack/Wearable/src/com.example.android.wearable.jumpingjack/fragments/SettingsFragment.java
rename to samples/browseable/JumpingJack/src/com.example.android.wearable.jumpingjack/fragments/SettingsFragment.java
diff --git a/samples/browseable/LNotifications/res/layout/activity_main.xml b/samples/browseable/LNotifications/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/LNotifications/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/MediaBrowserService/AndroidManifest.xml b/samples/browseable/MediaBrowserService/AndroidManifest.xml
index 6d05c27..45c8de7 100644
--- a/samples/browseable/MediaBrowserService/AndroidManifest.xml
+++ b/samples/browseable/MediaBrowserService/AndroidManifest.xml
@@ -36,7 +36,7 @@
android:resource="@xml/automotive_app_desc"/>
- <activity android:name="com.example.android.mediabrowserservice.MusicPlayerActivity"
+ <activity android:name=".MusicPlayerActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -51,8 +51,17 @@
android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification" />
+ <!--
+ (OPTIONAL) use this meta data to override the theme from which Android Auto will
+ look for colors. If you don't set this, Android Auto will look
+ for color attributes in your application theme.
+ -->
+ <meta-data
+ android:name="com.google.android.gms.car.application.theme"
+ android:resource="@style/CarTheme" />
+
<service
- android:name="com.example.android.mediabrowserservice.MusicService"
+ android:name=".MusicService"
android:exported="true"
>
<intent-filter>
diff --git a/samples/browseable/MediaBrowserService/res/drawable-hdpi/ic_notification.png b/samples/browseable/MediaBrowserService/res/drawable-hdpi/ic_notification.png
index d8ea5a9..a8cba40 100644
--- a/samples/browseable/MediaBrowserService/res/drawable-hdpi/ic_notification.png
+++ b/samples/browseable/MediaBrowserService/res/drawable-hdpi/ic_notification.png
Binary files differ
diff --git a/samples/browseable/MediaBrowserService/res/values-v21/styles.xml b/samples/browseable/MediaBrowserService/res/values-v21/styles.xml
deleted file mode 100644
index 21bb211..0000000
--- a/samples/browseable/MediaBrowserService/res/values-v21/styles.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 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="AppBaseTheme" parent="android:Theme.Material">
- <!-- colorPrimary is used for Notification icon and bottom facet bar icons
- and overflow actions -->
- <item name="android:colorPrimary">#ffff5722</item>
-
- <!-- colorPrimaryDark is used for background -->
- <item name="android:colorPrimaryDark">#ffbf360c</item>
-
- <!-- colorAccent is sparingly used for accents, like floating action button highlight,
- progress on playbar-->
- <item name="android:colorAccent">#ffff5722</item>
-
- </style>
-
-</resources>
diff --git a/samples/browseable/MediaBrowserService/res/values/strings.xml b/samples/browseable/MediaBrowserService/res/values/strings.xml
index 8c88fe3..9a4a50a 100644
--- a/samples/browseable/MediaBrowserService/res/values/strings.xml
+++ b/samples/browseable/MediaBrowserService/res/values/strings.xml
@@ -29,5 +29,6 @@
<string name="skip_previous">Skip to previous</string>
<string name="play_pause">play or pause</string>
<string name="skip_next">Skip to next</string>
+ <string name="no_search_results">No search results.</string>
</resources>
diff --git a/samples/browseable/MediaBrowserService/res/values/styles.xml b/samples/browseable/MediaBrowserService/res/values/styles.xml
index 3be59c1..35a3e7a 100644
--- a/samples/browseable/MediaBrowserService/res/values/styles.xml
+++ b/samples/browseable/MediaBrowserService/res/values/styles.xml
@@ -15,12 +15,27 @@
limitations under the License.
-->
<resources>
-
-
- <style name="AppTheme" parent="AppBaseTheme">
+ <style name="AppTheme" parent="android:Theme.Material">
+ <item name="android:colorPrimary">#ffff5722</item>
+ <item name="android:colorPrimaryDark">#ffbf360c</item>
+ <item name="android:colorAccent">#ffff5722</item>
</style>
- <style name="AppBaseTheme" parent="android:Theme.Light">
+ <style name="CarTheme" parent="AppTheme">
+ <!-- colorPrimaryDark is currently used in Android Auto for:
+ - App background
+ - Drawer right side ("more" custom actions) background
+ - Notification icon badge tinting
+ - Overview “now playing” icon tinting
+ -->
+ <item name="android:colorPrimaryDark">#ffbf360c</item>
+
+ <!-- colorAccent is used in Android Auto for:
+ - Spinner
+ - progress bar
+ - floating action button background (Play/Pause in media apps)
+ -->
+ <item name="android:colorAccent">#ffff5722</item>
</style>
</resources>
\ No newline at end of file
diff --git a/samples/browseable/MediaBrowserService/res/xml/allowed_media_browser_callers.xml b/samples/browseable/MediaBrowserService/res/xml/allowed_media_browser_callers.xml
new file mode 100644
index 0000000..5c326cf
--- /dev/null
+++ b/samples/browseable/MediaBrowserService/res/xml/allowed_media_browser_callers.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2014 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.
+-->
+<allowed_callers>
+ <signing_certificate name="Android Auto" release="false"
+ package="com.google.android.projection.gearhead">
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
+ VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+ VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
+ AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+ Fw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
+ A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
+ ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+ hvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR
+ 24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVy
+ xW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8X
+ W8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC
+ 69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexA
+ cKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkw
+ HQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0c
+ xb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
+ CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
+ QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
+ CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1Ud
+ EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrP
+ zgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXcla
+ XjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05a
+ IskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+a
+ ayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUW
+ Ev9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+ </signing_certificate>
+ <signing_certificate name="Android Auto" release="false"
+ package="com.google.android.projection.gearhead">
+ MIIDvTCCAqWgAwIBAgIJAOfkBvDXw5bzMA0GCSqGSIb3DQEBBQUAMHUxCzAJBgNV
+ BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+ aWV3MRQwEgYDVQQKDAtHb29nbGUgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDERMA8G
+ A1UEAwwIZ2VhcmhlYWQwHhcNMTQwNTI3MjMwMjUxWhcNNDExMDEyMjMwMjUxWjB1
+ MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
+ bnRhaW4gVmlldzEUMBIGA1UECgwLR29vZ2xlIEluYy4xEDAOBgNVBAsMB0FuZHJv
+ aWQxETAPBgNVBAMMCGdlYXJoZWFkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+ CgKCAQEAou7wwBKFyznqpRretJ3EVp55/Yr049Ag5wlGvrCnjIP8DrMrU+skfKe1
+ DmwpsLNtnhhiNH+J000Lok3hc8jdWKeKOopzKGDNvL/HvnS70Zyk26gj9jtMMHz9
+ 2aZdpmwD67FNmTlG2FERr+TwMD5agaPnsFR2zla6ugUvHGzz65YDxpCZsQ/TowyD
+ LnxgMagvhvS+Oex3yh2FN7pJfwS03KdGdkWPbLqf9Fem09s5jjeZW/O3RgnKoRPI
+ J4QLK70efjAZqJyBGcDZyQMwOs+8HIknraf8+cRZJDzqOx7rttl8M3KGB2EFljTp
+ 6/FyxJLnAo6QlXn7GrYalTI0yLU9dQIDAQABo1AwTjAdBgNVHQ4EFgQU9QPJ5xJE
+ DA8MDQMrj0hm2/A2BRkwHwYDVR0jBBgwFoAU9QPJ5xJEDA8MDQMrj0hm2/A2BRkw
+ DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEADcr5h1FR8IpmN4hSsUA9
+ SnCQVyXa1GQhzpQgRbF+npkgOn2Mebp8bd28VpfgooD2OBNQXCUcZkn7pWj++ut9
+ HhObHVaV5FNg0pdDqLna9QZ9Y4oS+ZrijK70XZ/EjlYUHvhu0pIjZAbD8CmCFlow
+ SR55qCSjM5iS37LZB32SMr1BBiYrNAvncKjYQVK8ctTRzhpNQQPBgXBA98Xl+d1D
+ Py00JWQuF0ssmhKcJuvfdEnFF7Hvaxz/gCQ9nzarQI3CJB8dOXVwF8mcyDRBz4JR
+ +YDpXo6BD+fGt15ov+zmqC8xaT9P1/JgoDXiMhy/6rwgdi9WxPf8mb7TnBC+CksX
+ 0A==
+ </signing_certificate>
+ <signing_certificate name="Android Auto" release="true"
+ package="com.google.android.projection.gearhead">
+ MIIDvTCCAqWgAwIBAgIJAMePnkuTQTAGMA0GCSqGSIb3DQEBBQUAMHUxCzAJBgNV
+ BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+ aWV3MRQwEgYDVQQKDAtHb29nbGUgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDERMA8G
+ A1UEAwwIZ2VhcmhlYWQwHhcNMTQwNTI3MjMwNTM0WhcNNDExMDEyMjMwNTM0WjB1
+ MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
+ bnRhaW4gVmlldzEUMBIGA1UECgwLR29vZ2xlIEluYy4xEDAOBgNVBAsMB0FuZHJv
+ aWQxETAPBgNVBAMMCGdlYXJoZWFkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+ CgKCAQEA050XDkNIsVRMX2wTvVplpCu4OtnyNK2v5B7PS+DggmH2yuZiwpTurdKD
+ Q9R9UzxH9U4lsC+mIxXkiBYKIWNVgMtiTgxkEy7cgWvdYHgNYpFu8IxZKYDyXes+
+ 02pfvpu63MIBD/PnvVFipo1oUrbfetj+mroEpjnA71gUS0Ok+H6XWWsmb8xFHQVM
+ oZWEIzsUJ2nhm8EcnPkAPfNZAG++XLPROoRQCaswyYsd42JuYAP3CwZuhDcUbMWm
+ k7rBi9BVQ8gmkrbwqo94A7qStLUp3NyCmlKSWHaZ05SspEPwsfctka0oXG5bhgT6
+ 67EMCzQ+YsFN1oJRL7Qq+mMQjFJs3wIDAQABo1AwTjAdBgNVHQ4EFgQUGvBfYNeu
+ 6JSJUnJZCiaBGsnXztswHwYDVR0jBBgwFoAUGvBfYNeu6JSJUnJZCiaBGsnXztsw
+ DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAlGsDY0EPu3NBSH5k6iw/
+ wJh9e3xMwS17ErKGlhyWogxJMzLjAN6g0aCPHxB40IQC+8qAl+RL7VQx6oxttf0m
+ 31yUGQPcNYbt2CxBTCAr885oLK5t2TAi5tQzhd6ZEYihWSUWUd/X8BQRouxboss9
+ QbBA/iIx0OpDaxiAcq7Cb67TheXZDxGuQ8fmHYbLx84pEvm3DQOB/LIMkkpQSfEC
+ 1f+oP1zB3urPU/dSvED/LCgOdrpxZ5di7SwSyue+Vq/TZQy34tPygEzD2d8hFlh/
+ yfhWkMizOeIXcayVAQdNn5zpBkuay1skGOjQQ5kTbDcDzigO2R2rqn6HCd9l5Z0W
+ IQ==
+ </signing_certificate>
+ <signing_certificate name="Media Browser Service Simulator" release="true"
+ package="com.google.android.mediasimulator">
+ MIIDvTCCAqWgAwIBAgIJAMePnkuTQTAGMA0GCSqGSIb3DQEBBQUAMHUxCzAJBgNV
+ BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+ aWV3MRQwEgYDVQQKDAtHb29nbGUgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDERMA8G
+ A1UEAwwIZ2VhcmhlYWQwHhcNMTQwNTI3MjMwNTM0WhcNNDExMDEyMjMwNTM0WjB1
+ MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
+ bnRhaW4gVmlldzEUMBIGA1UECgwLR29vZ2xlIEluYy4xEDAOBgNVBAsMB0FuZHJv
+ aWQxETAPBgNVBAMMCGdlYXJoZWFkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+ CgKCAQEA050XDkNIsVRMX2wTvVplpCu4OtnyNK2v5B7PS+DggmH2yuZiwpTurdKD
+ Q9R9UzxH9U4lsC+mIxXkiBYKIWNVgMtiTgxkEy7cgWvdYHgNYpFu8IxZKYDyXes+
+ 02pfvpu63MIBD/PnvVFipo1oUrbfetj+mroEpjnA71gUS0Ok+H6XWWsmb8xFHQVM
+ oZWEIzsUJ2nhm8EcnPkAPfNZAG++XLPROoRQCaswyYsd42JuYAP3CwZuhDcUbMWm
+ k7rBi9BVQ8gmkrbwqo94A7qStLUp3NyCmlKSWHaZ05SspEPwsfctka0oXG5bhgT6
+ 67EMCzQ+YsFN1oJRL7Qq+mMQjFJs3wIDAQABo1AwTjAdBgNVHQ4EFgQUGvBfYNeu
+ 6JSJUnJZCiaBGsnXztswHwYDVR0jBBgwFoAUGvBfYNeu6JSJUnJZCiaBGsnXztsw
+ DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAlGsDY0EPu3NBSH5k6iw/
+ wJh9e3xMwS17ErKGlhyWogxJMzLjAN6g0aCPHxB40IQC+8qAl+RL7VQx6oxttf0m
+ 31yUGQPcNYbt2CxBTCAr885oLK5t2TAi5tQzhd6ZEYihWSUWUd/X8BQRouxboss9
+ QbBA/iIx0OpDaxiAcq7Cb67TheXZDxGuQ8fmHYbLx84pEvm3DQOB/LIMkkpQSfEC
+ 1f+oP1zB3urPU/dSvED/LCgOdrpxZ5di7SwSyue+Vq/TZQy34tPygEzD2d8hFlh/
+ yfhWkMizOeIXcayVAQdNn5zpBkuay1skGOjQQ5kTbDcDzigO2R2rqn6HCd9l5Z0W
+ IQ==
+ </signing_certificate>
+ <signing_certificate name="Android Auto Simulator" release="true"
+ package="com.google.android.autosimulator">
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
+ VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+ VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
+ AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+ Fw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
+ A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
+ ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+ hvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR
+ 24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVy
+ xW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8X
+ W8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC
+ 69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexA
+ cKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkw
+ HQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0c
+ xb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
+ CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
+ QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
+ CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1Ud
+ EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrP
+ zgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXcla
+ XjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05a
+ IskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+a
+ ayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUW
+ Ev9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+ </signing_certificate>
+ <signing_certificate name="Media Browser Simulator" release="true"
+ package="com.google.android.mediasimulator">
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
+ VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+ VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
+ AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+ Fw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
+ A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
+ ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+ hvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR
+ 24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVy
+ xW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8X
+ W8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC
+ 69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexA
+ cKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkw
+ HQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0c
+ xb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
+ CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
+ QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
+ CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1Ud
+ EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrP
+ zgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXcla
+ XjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05a
+ IskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+a
+ ayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUW
+ Ev9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+ </signing_certificate>
+</allowed_callers>
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/AlbumArtCache.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/AlbumArtCache.java
new file mode 100644
index 0000000..4154381
--- /dev/null
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/AlbumArtCache.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 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.mediabrowserservice;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.util.LruCache;
+
+import com.example.android.mediabrowserservice.utils.BitmapHelper;
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import java.io.IOException;
+
+/**
+ * Implements a basic cache of album arts, with async loading support.
+ */
+public final class AlbumArtCache {
+ private static final String TAG = LogHelper.makeLogTag(AlbumArtCache.class);
+
+ private static final int MAX_ALBUM_ART_CACHE_SIZE = 12*1024*1024; // 12 MB
+ private static final int MAX_ART_WIDTH = 800; // pixels
+ private static final int MAX_ART_HEIGHT = 480; // pixels
+
+ // Resolution reasonable for carrying around as an icon (generally in
+ // MediaDescription.getIconBitmap). This should not be bigger than necessary, because
+ // the MediaDescription object should be lightweight. If you set it too high and try to
+ // serialize the MediaDescription, you may get FAILED BINDER TRANSACTION errors.
+ private static final int MAX_ART_WIDTH_ICON = 128; // pixels
+ private static final int MAX_ART_HEIGHT_ICON = 128; // pixels
+
+ private static final int BIG_BITMAP_INDEX = 0;
+ private static final int ICON_BITMAP_INDEX = 1;
+
+ private final LruCache<String, Bitmap[]> mCache;
+
+ private static final AlbumArtCache sInstance = new AlbumArtCache();
+
+ public static AlbumArtCache getInstance() {
+ return sInstance;
+ }
+
+ private AlbumArtCache() {
+ // Holds no more than MAX_ALBUM_ART_CACHE_SIZE bytes, bounded by maxmemory/4 and
+ // Integer.MAX_VALUE:
+ int maxSize = Math.min(MAX_ALBUM_ART_CACHE_SIZE,
+ (int) (Math.min(Integer.MAX_VALUE, Runtime.getRuntime().maxMemory()/4)));
+ mCache = new LruCache<String, Bitmap[]>(maxSize) {
+ @Override
+ protected int sizeOf(String key, Bitmap[] value) {
+ return value[BIG_BITMAP_INDEX].getByteCount()
+ + value[ICON_BITMAP_INDEX].getByteCount();
+ }
+ };
+ }
+
+ public Bitmap getBigImage(String artUrl) {
+ Bitmap[] result = mCache.get(artUrl);
+ return result == null ? null : result[BIG_BITMAP_INDEX];
+ }
+
+ public Bitmap getIconImage(String artUrl) {
+ Bitmap[] result = mCache.get(artUrl);
+ return result == null ? null : result[ICON_BITMAP_INDEX];
+ }
+
+ public void fetch(final String artUrl, final FetchListener listener) {
+ // WARNING: for the sake of simplicity, simultaneous multi-thread fetch requests
+ // are not handled properly: they may cause redundant costly operations, like HTTP
+ // requests and bitmap rescales. For production-level apps, we recommend you use
+ // a proper image loading library, like Glide.
+ Bitmap[] bitmap = mCache.get(artUrl);
+ if (bitmap != null) {
+ LogHelper.d(TAG, "getOrFetch: album art is in cache, using it", artUrl);
+ listener.onFetched(artUrl, bitmap[BIG_BITMAP_INDEX], bitmap[ICON_BITMAP_INDEX]);
+ return;
+ }
+ LogHelper.d(TAG, "getOrFetch: starting asynctask to fetch ", artUrl);
+
+ new AsyncTask<Void, Void, Bitmap[]>() {
+ @Override
+ protected Bitmap[] doInBackground(Void[] objects) {
+ Bitmap[] bitmaps;
+ try {
+ Bitmap bitmap = BitmapHelper.fetchAndRescaleBitmap(artUrl,
+ MAX_ART_WIDTH, MAX_ART_HEIGHT);
+ Bitmap icon = BitmapHelper.scaleBitmap(bitmap,
+ MAX_ART_WIDTH_ICON, MAX_ART_HEIGHT_ICON);
+ bitmaps = new Bitmap[] {bitmap, icon};
+ mCache.put(artUrl, bitmaps);
+ } catch (IOException e) {
+ return null;
+ }
+ LogHelper.d(TAG, "doInBackground: putting bitmap in cache. cache size=" +
+ mCache.size());
+ return bitmaps;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap[] bitmaps) {
+ if (bitmaps == null) {
+ listener.onError(artUrl, new IllegalArgumentException("got null bitmaps"));
+ } else {
+ listener.onFetched(artUrl,
+ bitmaps[BIG_BITMAP_INDEX], bitmaps[ICON_BITMAP_INDEX]);
+ }
+ }
+ }.execute();
+ }
+
+ public static abstract class FetchListener {
+ public abstract void onFetched(String artUrl, Bitmap bigImage, Bitmap iconImage);
+ public void onError(String artUrl, Exception e) {
+ LogHelper.e(TAG, e, "AlbumArtFetchListener: error while downloading " + artUrl);
+ }
+ }
+}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/BrowseFragment.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/BrowseFragment.java
index 726ae15..e4ccf76 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/BrowseFragment.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/BrowseFragment.java
@@ -47,7 +47,7 @@
*/
public class BrowseFragment extends Fragment {
- private static final String TAG = BrowseFragment.class.getSimpleName();
+ private static final String TAG = LogHelper.makeLogTag(BrowseFragment.class.getSimpleName());
public static final String ARG_MEDIA_ID = "media_id";
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MediaNotification.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MediaNotification.java
deleted file mode 100644
index 6872723..0000000
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MediaNotification.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2014 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.mediabrowserservice;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Color;
-import android.media.MediaDescription;
-import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
-import android.os.AsyncTask;
-import android.util.LruCache;
-
-import com.example.android.mediabrowserservice.utils.BitmapHelper;
-import com.example.android.mediabrowserservice.utils.LogHelper;
-
-import java.io.IOException;
-
-/**
- * Keeps track of a notification and updates it automatically for a given
- * MediaSession. Maintaining a visible notification (usually) guarantees that the music service
- * won't be killed during playback.
- */
-public class MediaNotification extends BroadcastReceiver {
- private static final String TAG = "MediaNotification";
-
- private static final int NOTIFICATION_ID = 412;
-
- public static final String ACTION_PAUSE = "com.example.android.mediabrowserservice.pause";
- public static final String ACTION_PLAY = "com.example.android.mediabrowserservice.play";
- public static final String ACTION_PREV = "com.example.android.mediabrowserservice.prev";
- public static final String ACTION_NEXT = "com.example.android.mediabrowserservice.next";
-
- private static final int MAX_ALBUM_ART_CACHE_SIZE = 1024*1024;
-
- private final MusicService mService;
- private MediaSession.Token mSessionToken;
- private MediaController mController;
- private MediaController.TransportControls mTransportControls;
- private final LruCache<String, Bitmap> mAlbumArtCache;
-
- private PlaybackState mPlaybackState;
- private MediaMetadata mMetadata;
-
- private Notification.Builder mNotificationBuilder;
- private NotificationManager mNotificationManager;
- private Notification.Action mPlayPauseAction;
-
- private PendingIntent mPauseIntent, mPlayIntent, mPreviousIntent, mNextIntent;
-
- private String mCurrentAlbumArt;
- private int mNotificationColor;
-
- private boolean mStarted = false;
-
- public MediaNotification(MusicService service) {
- mService = service;
- updateSessionToken();
-
- // simple album art cache that holds no more than
- // MAX_ALBUM_ART_CACHE_SIZE bytes:
- mAlbumArtCache = new LruCache<String, Bitmap>(MAX_ALBUM_ART_CACHE_SIZE) {
- @Override
- protected int sizeOf(String key, Bitmap value) {
- return value.getByteCount();
- }
- };
-
- mNotificationColor = getNotificationColor();
-
- mNotificationManager = (NotificationManager) mService
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- String pkg = mService.getPackageName();
- mPauseIntent = PendingIntent.getBroadcast(mService, 100,
- new Intent(ACTION_PAUSE).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
- mPlayIntent = PendingIntent.getBroadcast(mService, 100,
- new Intent(ACTION_PLAY).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
- mPreviousIntent = PendingIntent.getBroadcast(mService, 100,
- new Intent(ACTION_PREV).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
- mNextIntent = PendingIntent.getBroadcast(mService, 100,
- new Intent(ACTION_NEXT).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
- }
-
- protected int getNotificationColor() {
- int notificationColor = 0;
- String packageName = mService.getPackageName();
- try {
- Context packageContext = mService.createPackageContext(packageName, 0);
- ApplicationInfo applicationInfo =
- mService.getPackageManager().getApplicationInfo(packageName, 0);
- packageContext.setTheme(applicationInfo.theme);
- Resources.Theme theme = packageContext.getTheme();
- TypedArray ta = theme.obtainStyledAttributes(
- new int[] {android.R.attr.colorPrimary});
- notificationColor = ta.getColor(0, Color.DKGRAY);
- ta.recycle();
- } catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- }
- return notificationColor;
- }
-
- /**
- * Posts the notification and starts tracking the session to keep it
- * updated. The notification will automatically be removed if the session is
- * destroyed before {@link #stopNotification} is called.
- */
- public void startNotification() {
- if (!mStarted) {
- mController.registerCallback(mCb);
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_NEXT);
- filter.addAction(ACTION_PAUSE);
- filter.addAction(ACTION_PLAY);
- filter.addAction(ACTION_PREV);
- mService.registerReceiver(this, filter);
-
- mMetadata = mController.getMetadata();
- mPlaybackState = mController.getPlaybackState();
-
- mStarted = true;
- // The notification must be updated after setting started to true
- updateNotificationMetadata();
- }
- }
-
- /**
- * Removes the notification and stops tracking the session. If the session
- * was destroyed this has no effect.
- */
- public void stopNotification() {
- mStarted = false;
- mController.unregisterCallback(mCb);
- try {
- mNotificationManager.cancel(NOTIFICATION_ID);
- mService.unregisterReceiver(this);
- } catch (IllegalArgumentException ex) {
- // ignore if the receiver is not registered.
- }
- mService.stopForeground(true);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- LogHelper.d(TAG, "Received intent with action " + action);
- if (ACTION_PAUSE.equals(action)) {
- mTransportControls.pause();
- } else if (ACTION_PLAY.equals(action)) {
- mTransportControls.play();
- } else if (ACTION_NEXT.equals(action)) {
- mTransportControls.skipToNext();
- } else if (ACTION_PREV.equals(action)) {
- mTransportControls.skipToPrevious();
- }
- }
-
- /**
- * Update the state based on a change on the session token. Called either when
- * we are running for the first time or when the media session owner has destroyed the session
- * (see {@link android.media.session.MediaController.Callback#onSessionDestroyed()})
- */
- private void updateSessionToken() {
- MediaSession.Token freshToken = mService.getSessionToken();
- if (mSessionToken == null || !mSessionToken.equals(freshToken)) {
- if (mController != null) {
- mController.unregisterCallback(mCb);
- }
- mSessionToken = freshToken;
- mController = new MediaController(mService, mSessionToken);
- mTransportControls = mController.getTransportControls();
- if (mStarted) {
- mController.registerCallback(mCb);
- }
- }
- }
-
- private final MediaController.Callback mCb = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- mPlaybackState = state;
- LogHelper.d(TAG, "Received new playback state", state);
- updateNotificationPlaybackState();
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- mMetadata = metadata;
- LogHelper.d(TAG, "Received new metadata ", metadata);
- updateNotificationMetadata();
- }
-
- @Override
- public void onSessionDestroyed() {
- super.onSessionDestroyed();
- LogHelper.d(TAG, "Session was destroyed, resetting to the new session token");
- updateSessionToken();
- }
- };
-
- private void updateNotificationMetadata() {
- LogHelper.d(TAG, "updateNotificationMetadata. mMetadata=" + mMetadata);
- if (mMetadata == null || mPlaybackState == null) {
- return;
- }
-
- updatePlayPauseAction();
-
- mNotificationBuilder = new Notification.Builder(mService);
- int playPauseActionIndex = 0;
-
- // If skip to previous action is enabled
- if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
- mNotificationBuilder
- .addAction(R.drawable.ic_skip_previous_white_24dp,
- mService.getString(R.string.label_previous), mPreviousIntent);
- playPauseActionIndex = 1;
- }
-
- mNotificationBuilder.addAction(mPlayPauseAction);
-
- // If skip to next action is enabled
- if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
- mNotificationBuilder.addAction(R.drawable.ic_skip_next_white_24dp,
- mService.getString(R.string.label_next), mNextIntent);
- }
-
- MediaDescription description = mMetadata.getDescription();
-
- String fetchArtUrl = null;
- Bitmap art = description.getIconBitmap();
- if (art == null && description.getIconUri() != null) {
- // This sample assumes the iconUri will be a valid URL formatted String, but
- // it can actually be any valid Android Uri formatted String.
- // async fetch the album art icon
- String artUrl = description.getIconUri().toString();
- art = mAlbumArtCache.get(artUrl);
- if (art == null) {
- fetchArtUrl = artUrl;
- // use a placeholder art while the remote art is being downloaded
- art = BitmapFactory.decodeResource(mService.getResources(), R.drawable.ic_default_art);
- }
- }
-
- mNotificationBuilder
- .setStyle(new Notification.MediaStyle()
- .setShowActionsInCompactView(playPauseActionIndex) // only show play/pause in compact view
- .setMediaSession(mSessionToken))
- .setColor(mNotificationColor)
- .setSmallIcon(R.drawable.ic_notification)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setUsesChronometer(true)
- .setContentTitle(description.getTitle())
- .setContentText(description.getSubtitle())
- .setLargeIcon(art);
-
- updateNotificationPlaybackState();
-
- mService.startForeground(NOTIFICATION_ID, mNotificationBuilder.build());
- if (fetchArtUrl != null) {
- fetchBitmapFromURLAsync(fetchArtUrl);
- }
- }
-
- private void updatePlayPauseAction() {
- LogHelper.d(TAG, "updatePlayPauseAction");
- String label;
- int icon;
- PendingIntent intent;
- if (mPlaybackState.getState() == PlaybackState.STATE_PLAYING) {
- label = mService.getString(R.string.label_pause);
- icon = R.drawable.ic_pause_white_24dp;
- intent = mPauseIntent;
- } else {
- label = mService.getString(R.string.label_play);
- icon = R.drawable.ic_play_arrow_white_24dp;
- intent = mPlayIntent;
- }
- if (mPlayPauseAction == null) {
- mPlayPauseAction = new Notification.Action(icon, label, intent);
- } else {
- mPlayPauseAction.icon = icon;
- mPlayPauseAction.title = label;
- mPlayPauseAction.actionIntent = intent;
- }
- }
-
- private void updateNotificationPlaybackState() {
- LogHelper.d(TAG, "updateNotificationPlaybackState. mPlaybackState=" + mPlaybackState);
- if (mPlaybackState == null || !mStarted) {
- LogHelper.d(TAG, "updateNotificationPlaybackState. cancelling notification!");
- mService.stopForeground(true);
- return;
- }
- if (mNotificationBuilder == null) {
- LogHelper.d(TAG, "updateNotificationPlaybackState. there is no notificationBuilder. Ignoring request to update state!");
- return;
- }
- if (mPlaybackState.getPosition() >= 0) {
- LogHelper.d(TAG, "updateNotificationPlaybackState. updating playback position to ",
- (System.currentTimeMillis() - mPlaybackState.getPosition()) / 1000, " seconds");
- mNotificationBuilder
- .setWhen(System.currentTimeMillis() - mPlaybackState.getPosition())
- .setShowWhen(true)
- .setUsesChronometer(true);
- mNotificationBuilder.setShowWhen(true);
- } else {
- LogHelper.d(TAG, "updateNotificationPlaybackState. hiding playback position");
- mNotificationBuilder
- .setWhen(0)
- .setShowWhen(false)
- .setUsesChronometer(false);
- }
-
- updatePlayPauseAction();
-
- // Make sure that the notification can be dismissed by the user when we are not playing:
- mNotificationBuilder.setOngoing(mPlaybackState.getState() == PlaybackState.STATE_PLAYING);
-
- mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
- }
-
- public void fetchBitmapFromURLAsync(final String source) {
- LogHelper.d(TAG, "getBitmapFromURLAsync: starting asynctask to fetch ", source);
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void[] objects) {
- Bitmap bitmap = null;
- try {
- bitmap = BitmapHelper.fetchAndRescaleBitmap(source,
- BitmapHelper.MEDIA_ART_BIG_WIDTH, BitmapHelper.MEDIA_ART_BIG_HEIGHT);
- mAlbumArtCache.put(source, bitmap);
- } catch (IOException e) {
- LogHelper.e(TAG, e, "getBitmapFromURLAsync: " + source);
- }
- return bitmap;
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (bitmap != null && mMetadata != null &&
- mNotificationBuilder != null && mMetadata.getDescription() != null &&
- !source.equals(mMetadata.getDescription().getIconUri())) {
- // If the media is still the same, update the notification:
- LogHelper.d(TAG, "getBitmapFromURLAsync: set bitmap to ", source);
- mNotificationBuilder.setLargeIcon(bitmap);
- mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
- }
- }
- }.execute();
- }
-
-}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MediaNotificationManager.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MediaNotificationManager.java
new file mode 100644
index 0000000..59da728
--- /dev/null
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MediaNotificationManager.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2014 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.mediabrowserservice;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+import com.example.android.mediabrowserservice.utils.ResourceHelper;
+
+/**
+ * Keeps track of a notification and updates it automatically for a given
+ * MediaSession. Maintaining a visible notification (usually) guarantees that the music service
+ * won't be killed during playback.
+ */
+public class MediaNotificationManager extends BroadcastReceiver {
+ private static final String TAG = LogHelper.makeLogTag(MediaNotificationManager.class);
+
+ private static final int NOTIFICATION_ID = 412;
+ private static final int REQUEST_CODE = 100;
+
+ public static final String ACTION_PAUSE = "com.example.android.mediabrowserservice.pause";
+ public static final String ACTION_PLAY = "com.example.android.mediabrowserservice.play";
+ public static final String ACTION_PREV = "com.example.android.mediabrowserservice.prev";
+ public static final String ACTION_NEXT = "com.example.android.mediabrowserservice.next";
+
+ private final MusicService mService;
+ private MediaSession.Token mSessionToken;
+ private MediaController mController;
+ private MediaController.TransportControls mTransportControls;
+
+ private PlaybackState mPlaybackState;
+ private MediaMetadata mMetadata;
+
+ private NotificationManager mNotificationManager;
+
+ private PendingIntent mPauseIntent;
+ private PendingIntent mPlayIntent;
+ private PendingIntent mPreviousIntent;
+ private PendingIntent mNextIntent;
+
+ private int mNotificationColor;
+
+ private boolean mStarted = false;
+
+ public MediaNotificationManager(MusicService service) {
+ mService = service;
+ updateSessionToken();
+
+ mNotificationColor = ResourceHelper.getThemeColor(mService,
+ android.R.attr.colorPrimary, Color.DKGRAY);
+
+ mNotificationManager = (NotificationManager) mService
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ String pkg = mService.getPackageName();
+ mPauseIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
+ new Intent(ACTION_PAUSE).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+ mPlayIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
+ new Intent(ACTION_PLAY).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+ mPreviousIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
+ new Intent(ACTION_PREV).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+ mNextIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
+ new Intent(ACTION_NEXT).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
+
+ // Cancel all notifications to handle the case where the Service was killed and
+ // restarted by the system.
+ mNotificationManager.cancelAll();
+ }
+
+ /**
+ * Posts the notification and starts tracking the session to keep it
+ * updated. The notification will automatically be removed if the session is
+ * destroyed before {@link #stopNotification} is called.
+ */
+ public void startNotification() {
+ if (!mStarted) {
+ mMetadata = mController.getMetadata();
+ mPlaybackState = mController.getPlaybackState();
+
+ // The notification must be updated after setting started to true
+ Notification notification = createNotification();
+ if (notification != null) {
+ mController.registerCallback(mCb);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_NEXT);
+ filter.addAction(ACTION_PAUSE);
+ filter.addAction(ACTION_PLAY);
+ filter.addAction(ACTION_PREV);
+ mService.registerReceiver(this, filter);
+
+ mService.startForeground(NOTIFICATION_ID, notification);
+ mStarted = true;
+ }
+ }
+ }
+
+ /**
+ * Removes the notification and stops tracking the session. If the session
+ * was destroyed this has no effect.
+ */
+ public void stopNotification() {
+ if (mStarted) {
+ mStarted = false;
+ mController.unregisterCallback(mCb);
+ try {
+ mNotificationManager.cancel(NOTIFICATION_ID);
+ mService.unregisterReceiver(this);
+ } catch (IllegalArgumentException ex) {
+ // ignore if the receiver is not registered.
+ }
+ mService.stopForeground(true);
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ LogHelper.d(TAG, "Received intent with action " + action);
+ switch (action) {
+ case ACTION_PAUSE:
+ mTransportControls.pause();
+ break;
+ case ACTION_PLAY:
+ mTransportControls.play();
+ break;
+ case ACTION_NEXT:
+ mTransportControls.skipToNext();
+ break;
+ case ACTION_PREV:
+ mTransportControls.skipToPrevious();
+ break;
+ default:
+ LogHelper.w(TAG, "Unknown intent ignored. Action=", action);
+ }
+ }
+
+ /**
+ * Update the state based on a change on the session token. Called either when
+ * we are running for the first time or when the media session owner has destroyed the session
+ * (see {@link android.media.session.MediaController.Callback#onSessionDestroyed()})
+ */
+ private void updateSessionToken() {
+ MediaSession.Token freshToken = mService.getSessionToken();
+ if (mSessionToken == null || !mSessionToken.equals(freshToken)) {
+ if (mController != null) {
+ mController.unregisterCallback(mCb);
+ }
+ mSessionToken = freshToken;
+ mController = new MediaController(mService, mSessionToken);
+ mTransportControls = mController.getTransportControls();
+ if (mStarted) {
+ mController.registerCallback(mCb);
+ }
+ }
+ }
+
+ private PendingIntent createContentIntent() {
+ Intent openUI = new Intent(mService, MusicPlayerActivity.class);
+ openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ return PendingIntent.getActivity(mService, REQUEST_CODE, openUI,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ private final MediaController.Callback mCb = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ mPlaybackState = state;
+ LogHelper.d(TAG, "Received new playback state", state);
+ if (state != null && (state.getState() == PlaybackState.STATE_STOPPED ||
+ state.getState() == PlaybackState.STATE_NONE)) {
+ stopNotification();
+ } else {
+ Notification notification = createNotification();
+ if (notification != null) {
+ mNotificationManager.notify(NOTIFICATION_ID, notification);
+ }
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ mMetadata = metadata;
+ LogHelper.d(TAG, "Received new metadata ", metadata);
+ Notification notification = createNotification();
+ if (notification != null) {
+ mNotificationManager.notify(NOTIFICATION_ID, notification);
+ }
+ }
+
+ @Override
+ public void onSessionDestroyed() {
+ super.onSessionDestroyed();
+ LogHelper.d(TAG, "Session was destroyed, resetting to the new session token");
+ updateSessionToken();
+ }
+ };
+
+ private Notification createNotification() {
+ LogHelper.d(TAG, "updateNotificationMetadata. mMetadata=" + mMetadata);
+ if (mMetadata == null || mPlaybackState == null) {
+ return null;
+ }
+
+ Notification.Builder notificationBuilder = new Notification.Builder(mService);
+ int playPauseButtonPosition = 0;
+
+ // If skip to previous action is enabled
+ if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
+ notificationBuilder.addAction(R.drawable.ic_skip_previous_white_24dp,
+ mService.getString(R.string.label_previous), mPreviousIntent);
+
+ // If there is a "skip to previous" button, the play/pause button will
+ // be the second one. We need to keep track of it, because the MediaStyle notification
+ // requires to specify the index of the buttons (actions) that should be visible
+ // when in compact view.
+ playPauseButtonPosition = 1;
+ }
+
+ addPlayPauseAction(notificationBuilder);
+
+ // If skip to next action is enabled
+ if ((mPlaybackState.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
+ notificationBuilder.addAction(R.drawable.ic_skip_next_white_24dp,
+ mService.getString(R.string.label_next), mNextIntent);
+ }
+
+ MediaDescription description = mMetadata.getDescription();
+
+ String fetchArtUrl = null;
+ Bitmap art = null;
+ if (description.getIconUri() != null) {
+ // This sample assumes the iconUri will be a valid URL formatted String, but
+ // it can actually be any valid Android Uri formatted String.
+ // async fetch the album art icon
+ String artUrl = description.getIconUri().toString();
+ art = AlbumArtCache.getInstance().getBigImage(artUrl);
+ if (art == null) {
+ fetchArtUrl = artUrl;
+ // use a placeholder art while the remote art is being downloaded
+ art = BitmapFactory.decodeResource(mService.getResources(),
+ R.drawable.ic_default_art);
+ }
+ }
+
+ notificationBuilder
+ .setStyle(new Notification.MediaStyle()
+ .setShowActionsInCompactView(
+ new int[]{playPauseButtonPosition}) // show only play/pause in compact view
+ .setMediaSession(mSessionToken))
+ .setColor(mNotificationColor)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setUsesChronometer(true)
+ .setContentIntent(createContentIntent())
+ .setContentTitle(description.getTitle())
+ .setContentText(description.getSubtitle())
+ .setLargeIcon(art);
+
+ setNotificationPlaybackState(notificationBuilder);
+ if (fetchArtUrl != null) {
+ fetchBitmapFromURLAsync(fetchArtUrl, notificationBuilder);
+ }
+
+ return notificationBuilder.build();
+ }
+
+ private void addPlayPauseAction(Notification.Builder builder) {
+ LogHelper.d(TAG, "updatePlayPauseAction");
+ String label;
+ int icon;
+ PendingIntent intent;
+ if (mPlaybackState.getState() == PlaybackState.STATE_PLAYING) {
+ label = mService.getString(R.string.label_pause);
+ icon = R.drawable.ic_pause_white_24dp;
+ intent = mPauseIntent;
+ } else {
+ label = mService.getString(R.string.label_play);
+ icon = R.drawable.ic_play_arrow_white_24dp;
+ intent = mPlayIntent;
+ }
+ builder.addAction(new Notification.Action(icon, label, intent));
+ }
+
+ private void setNotificationPlaybackState(Notification.Builder builder) {
+ LogHelper.d(TAG, "updateNotificationPlaybackState. mPlaybackState=" + mPlaybackState);
+ if (mPlaybackState == null || !mStarted) {
+ LogHelper.d(TAG, "updateNotificationPlaybackState. cancelling notification!");
+ mService.stopForeground(true);
+ return;
+ }
+ if (mPlaybackState.getState() == PlaybackState.STATE_PLAYING
+ && mPlaybackState.getPosition() >= 0) {
+ LogHelper.d(TAG, "updateNotificationPlaybackState. updating playback position to ",
+ (System.currentTimeMillis() - mPlaybackState.getPosition()) / 1000, " seconds");
+ builder
+ .setWhen(System.currentTimeMillis() - mPlaybackState.getPosition())
+ .setShowWhen(true)
+ .setUsesChronometer(true);
+ } else {
+ LogHelper.d(TAG, "updateNotificationPlaybackState. hiding playback position");
+ builder
+ .setWhen(0)
+ .setShowWhen(false)
+ .setUsesChronometer(false);
+ }
+
+ // Make sure that the notification can be dismissed by the user when we are not playing:
+ builder.setOngoing(mPlaybackState.getState() == PlaybackState.STATE_PLAYING);
+ }
+
+ private void fetchBitmapFromURLAsync(final String bitmapUrl,
+ final Notification.Builder builder) {
+ AlbumArtCache.getInstance().fetch(bitmapUrl, new AlbumArtCache.FetchListener() {
+ @Override
+ public void onFetched(String artUrl, Bitmap bitmap, Bitmap icon) {
+ if (mMetadata != null && mMetadata.getDescription() != null &&
+ artUrl.equals(mMetadata.getDescription().getIconUri().toString())) {
+ // If the media is still the same, update the notification:
+ LogHelper.d(TAG, "fetchBitmapFromURLAsync: set bitmap to ", artUrl);
+ builder.setLargeIcon(bitmap);
+ mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+ }
+ }
+ });
+ }
+}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicPlayerActivity.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicPlayerActivity.java
index 394b01a..ac231c7 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicPlayerActivity.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicPlayerActivity.java
@@ -25,8 +25,6 @@
public class MusicPlayerActivity extends Activity
implements BrowseFragment.FragmentDataHelper {
- private static final String TAG = MusicPlayerActivity.class.getSimpleName();
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicService.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicService.java
index b482426..3b3ec83 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicService.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/MusicService.java
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,932 +16,725 @@
package com.example.android.mediabrowserservice;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.media.MediaDescription;
-import android.media.MediaMetadata;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.media.MediaPlayer.OnErrorListener;
-import android.media.MediaPlayer.OnPreparedListener;
-import android.media.browse.MediaBrowser;
-import android.media.browse.MediaBrowser.MediaItem;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
-import android.net.Uri;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiManager.WifiLock;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.service.media.MediaBrowserService;
+ import android.app.PendingIntent;
+ import android.content.Context;
+ import android.content.Intent;
+ import android.graphics.Bitmap;
+ import android.media.MediaDescription;
+ import android.media.MediaMetadata;
+ import android.media.browse.MediaBrowser.MediaItem;
+ import android.media.session.MediaSession;
+ import android.media.session.PlaybackState;
+ import android.net.Uri;
+ import android.os.Bundle;
+ import android.os.Handler;
+ import android.os.Message;
+ import android.os.SystemClock;
+ import android.service.media.MediaBrowserService;
+ import android.text.TextUtils;
-import com.example.android.mediabrowserservice.model.MusicProvider;
-import com.example.android.mediabrowserservice.utils.LogHelper;
-import com.example.android.mediabrowserservice.utils.MediaIDHelper;
-import com.example.android.mediabrowserservice.utils.QueueHelper;
+ import com.example.android.mediabrowserservice.model.MusicProvider;
+ import com.example.android.mediabrowserservice.utils.CarHelper;
+ import com.example.android.mediabrowserservice.utils.LogHelper;
+ import com.example.android.mediabrowserservice.utils.MediaIDHelper;
+ import com.example.android.mediabrowserservice.utils.QueueHelper;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+ import java.lang.ref.WeakReference;
+ import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.List;
-import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
-import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_ROOT;
-import static com.example.android.mediabrowserservice.utils.MediaIDHelper.createBrowseCategoryMediaID;
-import static com.example.android.mediabrowserservice.utils.MediaIDHelper.extractBrowseCategoryFromMediaID;
+ import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
+ import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_ROOT;
+ import static com.example.android.mediabrowserservice.utils.MediaIDHelper.createBrowseCategoryMediaID;
-/**
- * This class provides a MediaBrowser through a service. It exposes the media library to a browsing
- * client, through the onGetRoot and onLoadChildren methods. It also creates a MediaSession and
- * exposes it through its MediaSession.Token, which allows the client to create a MediaController
- * that connects to and send control commands to the MediaSession remotely. This is useful for
- * user interfaces that need to interact with your media session, like Android Auto. You can
- * (should) also use the same service from your app's UI, which gives a seamless playback
- * experience to the user.
- *
- * To implement a MediaBrowserService, you need to:
- *
- * <ul>
- *
- * <li> Extend {@link android.service.media.MediaBrowserService}, implementing the media browsing
- * related methods {@link android.service.media.MediaBrowserService#onGetRoot} and
- * {@link android.service.media.MediaBrowserService#onLoadChildren};
- * <li> In onCreate, start a new {@link android.media.session.MediaSession} and notify its parent
- * with the session's token {@link android.service.media.MediaBrowserService#setSessionToken};
- *
- * <li> Set a callback on the
- * {@link android.media.session.MediaSession#setCallback(android.media.session.MediaSession.Callback)}.
- * The callback will receive all the user's actions, like play, pause, etc;
- *
- * <li> Handle all the actual music playing using any method your app prefers (for example,
- * {@link android.media.MediaPlayer})
- *
- * <li> Update playbackState, "now playing" metadata and queue, using MediaSession proper methods
- * {@link android.media.session.MediaSession#setPlaybackState(android.media.session.PlaybackState)}
- * {@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata)} and
- * {@link android.media.session.MediaSession#setQueue(java.util.List)})
- *
- * <li> Declare and export the service in AndroidManifest with an intent receiver for the action
- * android.media.browse.MediaBrowserService
- *
- * </ul>
- *
- * To make your app compatible with Android Auto, you also need to:
- *
- * <ul>
- *
- * <li> Declare a meta-data tag in AndroidManifest.xml linking to a xml resource
- * with a <automotiveApp> root element. For a media app, this must include
- * an <uses name="media"/> element as a child.
- * For example, in AndroidManifest.xml:
- * <meta-data android:name="com.google.android.gms.car.application"
- * android:resource="@xml/automotive_app_desc"/>
- * And in res/values/automotive_app_desc.xml:
- * <automotiveApp>
- * <uses name="media"/>
- * </automotiveApp>
- *
- * </ul>
+ /**
+ * This class provides a MediaBrowser through a service. It exposes the media library to a browsing
+ * client, through the onGetRoot and onLoadChildren methods. It also creates a MediaSession and
+ * exposes it through its MediaSession.Token, which allows the client to create a MediaController
+ * that connects to and send control commands to the MediaSession remotely. This is useful for
+ * user interfaces that need to interact with your media session, like Android Auto. You can
+ * (should) also use the same service from your app's UI, which gives a seamless playback
+ * experience to the user.
+ *
+ * To implement a MediaBrowserService, you need to:
+ *
+ * <ul>
+ *
+ * <li> Extend {@link android.service.media.MediaBrowserService}, implementing the media browsing
+ * related methods {@link android.service.media.MediaBrowserService#onGetRoot} and
+ * {@link android.service.media.MediaBrowserService#onLoadChildren};
+ * <li> In onCreate, start a new {@link android.media.session.MediaSession} and notify its parent
+ * with the session's token {@link android.service.media.MediaBrowserService#setSessionToken};
+ *
+ * <li> Set a callback on the
+ * {@link android.media.session.MediaSession#setCallback(android.media.session.MediaSession.Callback)}.
+ * The callback will receive all the user's actions, like play, pause, etc;
+ *
+ * <li> Handle all the actual music playing using any method your app prefers (for example,
+ * {@link android.media.MediaPlayer})
+ *
+ * <li> Update playbackState, "now playing" metadata and queue, using MediaSession proper methods
+ * {@link android.media.session.MediaSession#setPlaybackState(android.media.session.PlaybackState)}
+ * {@link android.media.session.MediaSession#setMetadata(android.media.MediaMetadata)} and
+ * {@link android.media.session.MediaSession#setQueue(java.util.List)})
+ *
+ * <li> Declare and export the service in AndroidManifest with an intent receiver for the action
+ * android.media.browse.MediaBrowserService
+ *
+ * </ul>
+ *
+ * To make your app compatible with Android Auto, you also need to:
+ *
+ * <ul>
+ *
+ * <li> Declare a meta-data tag in AndroidManifest.xml linking to a xml resource
+ * with a <automotiveApp> root element. For a media app, this must include
+ * an <uses name="media"/> element as a child.
+ * For example, in AndroidManifest.xml:
+ * <meta-data android:name="com.google.android.gms.car.application"
+ * android:resource="@xml/automotive_app_desc"/>
+ * And in res/values/automotive_app_desc.xml:
+ * <automotiveApp>
+ * <uses name="media"/>
+ * </automotiveApp>
+ *
+ * </ul>
- * @see <a href="README.md">README.md</a> for more details.
- *
- */
+ * @see <a href="README.md">README.md</a> for more details.
+ *
+ */
-public class MusicService extends MediaBrowserService implements OnPreparedListener,
- OnCompletionListener, OnErrorListener, AudioManager.OnAudioFocusChangeListener {
+ public class MusicService extends MediaBrowserService implements Playback.Callback {
- private static final String TAG = "MusicService";
+ // The action of the incoming Intent indicating that it contains a command
+ // to be executed (see {@link #onStartCommand})
+ public static final String ACTION_CMD = "com.example.android.mediabrowserservice.ACTION_CMD";
+ // The key in the extras of the incoming Intent indicating the command that
+ // should be executed (see {@link #onStartCommand})
+ public static final String CMD_NAME = "CMD_NAME";
+ // A value of a CMD_NAME key in the extras of the incoming Intent that
+ // indicates that the music playback should be paused (see {@link #onStartCommand})
+ public static final String CMD_PAUSE = "CMD_PAUSE";
- // Action to thumbs up a media item
- private static final String CUSTOM_ACTION_THUMBS_UP = "thumbs_up";
- // Delay stopSelf by using a handler.
- private static final int STOP_DELAY = 30000;
+ private static final String TAG = LogHelper.makeLogTag(MusicService.class);
+ // Action to thumbs up a media item
+ private static final String CUSTOM_ACTION_THUMBS_UP =
+ "com.example.android.mediabrowserservice.THUMBS_UP";
+ // Delay stopSelf by using a handler.
+ private static final int STOP_DELAY = 30000;
- // The volume we set the media player to when we lose audio focus, but are
- // allowed to reduce the volume instead of stopping playback.
- public static final float VOLUME_DUCK = 0.2f;
+ // Music catalog manager
+ private MusicProvider mMusicProvider;
+ private MediaSession mSession;
+ // "Now playing" queue:
+ private List<MediaSession.QueueItem> mPlayingQueue;
+ private int mCurrentIndexOnQueue;
+ private MediaNotificationManager mMediaNotificationManager;
+ // Indicates whether the service was started.
+ private boolean mServiceStarted;
+ private DelayedStopHandler mDelayedStopHandler = new DelayedStopHandler(this);
+ private Playback mPlayback;
+ private PackageValidator mPackageValidator;
- // The volume we set the media player when we have audio focus.
- public static final float VOLUME_NORMAL = 1.0f;
- public static final String ANDROID_AUTO_PACKAGE_NAME = "com.google.android.projection.gearhead";
- public static final String ANDROID_AUTO_SIMULATOR_PACKAGE_NAME = "com.google.android.mediasimulator";
+ /*
+ * (non-Javadoc)
+ * @see android.app.Service#onCreate()
+ */
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogHelper.d(TAG, "onCreate");
- // Music catalog manager
- private MusicProvider mMusicProvider;
+ mPlayingQueue = new ArrayList<>();
+ mMusicProvider = new MusicProvider();
+ mPackageValidator = new PackageValidator(this);
- private MediaSession mSession;
- private MediaPlayer mMediaPlayer;
+ // Start a new MediaSession
+ mSession = new MediaSession(this, "MusicService");
+ setSessionToken(mSession.getSessionToken());
+ mSession.setCallback(new MediaSessionCallback());
+ mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
+ MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
- // "Now playing" queue:
- private List<MediaSession.QueueItem> mPlayingQueue;
- private int mCurrentIndexOnQueue;
+ mPlayback = new Playback(this, mMusicProvider);
+ mPlayback.setState(PlaybackState.STATE_NONE);
+ mPlayback.setCallback(this);
+ mPlayback.start();
- // Current local media player state
- private int mState = PlaybackState.STATE_NONE;
+ Context context = getApplicationContext();
+ Intent intent = new Intent(context, MusicPlayerActivity.class);
+ PendingIntent pi = PendingIntent.getActivity(context, 99 /*request code*/,
+ intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ mSession.setSessionActivity(pi);
- // Wifi lock that we hold when streaming files from the internet, in order
- // to prevent the device from shutting off the Wifi radio
- private WifiLock mWifiLock;
+ Bundle extras = new Bundle();
+ CarHelper.setSlotReservationFlags(extras, true, true, true);
+ mSession.setExtras(extras);
- private MediaNotification mMediaNotification;
+ updatePlaybackState(null);
- // Indicates whether the service was started.
- private boolean mServiceStarted;
+ mMediaNotificationManager = new MediaNotificationManager(this);
+ }
- enum AudioFocus {
- NoFocusNoDuck, // we don't have audio focus, and can't duck
- NoFocusCanDuck, // we don't have focus, but can play at a low volume
- // ("ducking")
- Focused // we have full audio focus
- }
+ /**
+ * (non-Javadoc)
+ * @see android.app.Service#onStartCommand(android.content.Intent, int, int)
+ */
+ @Override
+ public int onStartCommand(Intent startIntent, int flags, int startId) {
+ if (startIntent != null) {
+ String action = startIntent.getAction();
+ String command = startIntent.getStringExtra(CMD_NAME);
+ if (ACTION_CMD.equals(action)) {
+ if (CMD_PAUSE.equals(command)) {
+ if (mPlayback != null && mPlayback.isPlaying()) {
+ handlePauseRequest();
+ }
+ }
+ }
+ }
+ return START_STICKY;
+ }
- // Type of audio focus we have:
- private AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck;
- private AudioManager mAudioManager;
+ /**
+ * (non-Javadoc)
+ * @see android.app.Service#onDestroy()
+ */
+ @Override
+ public void onDestroy() {
+ LogHelper.d(TAG, "onDestroy");
+ // Service is being killed, so make sure we release our resources
+ handleStopRequest(null);
- // Indicates if we should start playing immediately after we gain focus.
- private boolean mPlayOnFocusGain;
+ mDelayedStopHandler.removeCallbacksAndMessages(null);
+ // Always release the MediaSession to clean up resources
+ // and notify associated MediaController(s).
+ mSession.release();
+ }
- private Handler mDelayedStopHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if ((mMediaPlayer != null && mMediaPlayer.isPlaying()) ||
- mPlayOnFocusGain) {
- LogHelper.d(TAG, "Ignoring delayed stop since the media player is in use.");
- return;
- }
- LogHelper.d(TAG, "Stopping service with delay handler.");
- stopSelf();
- mServiceStarted = false;
- }
- };
+ @Override
+ public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ LogHelper.d(TAG, "OnGetRoot: clientPackageName=" + clientPackageName,
+ "; clientUid=" + clientUid + " ; rootHints=", rootHints);
+ // To ensure you are not allowing any arbitrary app to browse your app's contents, you
+ // need to check the origin:
+ if (!mPackageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
+ // If the request comes from an untrusted package, return null. No further calls will
+ // be made to other media browsing methods.
+ LogHelper.w(TAG, "OnGetRoot: IGNORING request from untrusted package "
+ + clientPackageName);
+ return null;
+ }
+ //noinspection StatementWithEmptyBody
+ if (CarHelper.isValidCarPackage(clientPackageName)) {
+ // Optional: if your app needs to adapt ads, music library or anything else that
+ // needs to run differently when connected to the car, this is where you should handle
+ // it.
+ }
+ return new BrowserRoot(MEDIA_ID_ROOT, null);
+ }
- /*
- * (non-Javadoc)
- * @see android.app.Service#onCreate()
- */
- @Override
- public void onCreate() {
- super.onCreate();
- LogHelper.d(TAG, "onCreate");
+ @Override
+ public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+ if (!mMusicProvider.isInitialized()) {
+ // Use result.detach to allow calling result.sendResult from another thread:
+ result.detach();
- mPlayingQueue = new ArrayList<>();
+ mMusicProvider.retrieveMediaAsync(new MusicProvider.Callback() {
+ @Override
+ public void onMusicCatalogReady(boolean success) {
+ if (success) {
+ loadChildrenImpl(parentMediaId, result);
+ } else {
+ updatePlaybackState(getString(R.string.error_no_metadata));
+ result.sendResult(Collections.<MediaItem>emptyList());
+ }
+ }
+ });
- // Create the Wifi lock (this does not acquire the lock, this just creates it)
- mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL, "MusicDemo_lock");
+ } else {
+ // If our music catalog is already loaded/cached, load them into result immediately
+ loadChildrenImpl(parentMediaId, result);
+ }
+ }
+ /**
+ * Actual implementation of onLoadChildren that assumes that MusicProvider is already
+ * initialized.
+ */
+ private void loadChildrenImpl(final String parentMediaId,
+ final Result<List<MediaItem>> result) {
+ LogHelper.d(TAG, "OnLoadChildren: parentMediaId=", parentMediaId);
- // Create the music catalog metadata provider
- mMusicProvider = new MusicProvider();
- mMusicProvider.retrieveMedia(new MusicProvider.Callback() {
- @Override
- public void onMusicCatalogReady(boolean success) {
- mState = success ? PlaybackState.STATE_NONE : PlaybackState.STATE_ERROR;
- }
- });
+ List<MediaItem> mediaItems = new ArrayList<>();
- mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ if (MEDIA_ID_ROOT.equals(parentMediaId)) {
+ LogHelper.d(TAG, "OnLoadChildren.ROOT");
+ mediaItems.add(new MediaItem(
+ new MediaDescription.Builder()
+ .setMediaId(MEDIA_ID_MUSICS_BY_GENRE)
+ .setTitle(getString(R.string.browse_genres))
+ .setIconUri(Uri.parse("android.resource://" +
+ "com.example.android.mediabrowserservice/drawable/ic_by_genre"))
+ .setSubtitle(getString(R.string.browse_genre_subtitle))
+ .build(), MediaItem.FLAG_BROWSABLE
+ ));
- // Start a new MediaSession
- mSession = new MediaSession(this, "MusicService");
- setSessionToken(mSession.getSessionToken());
- mSession.setCallback(new MediaSessionCallback());
- mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
- MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ } else if (MEDIA_ID_MUSICS_BY_GENRE.equals(parentMediaId)) {
+ LogHelper.d(TAG, "OnLoadChildren.GENRES");
+ for (String genre : mMusicProvider.getGenres()) {
+ MediaItem item = new MediaItem(
+ new MediaDescription.Builder()
+ .setMediaId(createBrowseCategoryMediaID(MEDIA_ID_MUSICS_BY_GENRE, genre))
+ .setTitle(genre)
+ .setSubtitle(getString(R.string.browse_musics_by_genre_subtitle, genre))
+ .build(), MediaItem.FLAG_BROWSABLE
+ );
+ mediaItems.add(item);
+ }
- // Use these extras to reserve space for the corresponding actions, even when they are disabled
- // in the playbackstate, so the custom actions don't reflow.
- Bundle extras = new Bundle();
- extras.putBoolean(
- "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT",
- true);
- extras.putBoolean(
- "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS",
- true);
- // If you want to reserve the Queue slot when there is no queue
- // (mSession.setQueue(emptylist)), uncomment the lines below:
- // extras.putBoolean(
- // "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE",
- // true);
- mSession.setExtras(extras);
+ } else if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_GENRE)) {
+ String genre = MediaIDHelper.getHierarchy(parentMediaId)[1];
+ LogHelper.d(TAG, "OnLoadChildren.SONGS_BY_GENRE genre=", genre);
+ for (MediaMetadata track : mMusicProvider.getMusicsByGenre(genre)) {
+ // Since mediaMetadata fields are immutable, we need to create a copy, so we
+ // can set a hierarchy-aware mediaID. We will need to know the media hierarchy
+ // when we get a onPlayFromMusicID call, so we can create the proper queue based
+ // on where the music was selected from (by artist, by genre, random, etc)
+ String hierarchyAwareMediaID = MediaIDHelper.createMediaID(
+ track.getDescription().getMediaId(), MEDIA_ID_MUSICS_BY_GENRE, genre);
+ MediaMetadata trackCopy = new MediaMetadata.Builder(track)
+ .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
+ .build();
+ MediaItem bItem = new MediaItem(
+ trackCopy.getDescription(), MediaItem.FLAG_PLAYABLE);
+ mediaItems.add(bItem);
+ }
+ } else {
+ LogHelper.w(TAG, "Skipping unmatched parentMediaId: ", parentMediaId);
+ }
+ LogHelper.d(TAG, "OnLoadChildren sending ", mediaItems.size(),
+ " results for ", parentMediaId);
+ result.sendResult(mediaItems);
+ }
- updatePlaybackState(null);
+ private final class MediaSessionCallback extends MediaSession.Callback {
+ @Override
+ public void onPlay() {
+ LogHelper.d(TAG, "play");
- mMediaNotification = new MediaNotification(this);
- }
+ if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
+ mPlayingQueue = QueueHelper.getRandomQueue(mMusicProvider);
+ mSession.setQueue(mPlayingQueue);
+ mSession.setQueueTitle(getString(R.string.random_queue_title));
+ // start playing from the beginning of the queue
+ mCurrentIndexOnQueue = 0;
+ }
- /*
- * (non-Javadoc)
- * @see android.app.Service#onDestroy()
- */
- @Override
- public void onDestroy() {
- LogHelper.d(TAG, "onDestroy");
+ if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+ handlePlayRequest();
+ }
+ }
- // Service is being killed, so make sure we release our resources
- handleStopRequest(null);
+ @Override
+ public void onSkipToQueueItem(long queueId) {
+ LogHelper.d(TAG, "OnSkipToQueueItem:" + queueId);
- mDelayedStopHandler.removeCallbacksAndMessages(null);
- // In particular, always release the MediaSession to clean up resources
- // and notify associated MediaController(s).
- mSession.release();
- }
+ if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+ // set the current index on queue from the music Id:
+ mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(mPlayingQueue, queueId);
+ // play the music
+ handlePlayRequest();
+ }
+ }
+ @Override
+ public void onSeekTo(long position) {
+ LogHelper.d(TAG, "onSeekTo:", position);
+ mPlayback.seekTo((int) position);
+ }
- @Override
- public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
- LogHelper.d(TAG, "OnGetRoot: clientPackageName=" + clientPackageName,
- "; clientUid=" + clientUid + " ; rootHints=", rootHints);
- // To ensure you are not allowing any arbitrary app to browse your app's contents, you
- // need to check the origin:
- if (!PackageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
- // If the request comes from an untrusted package, return null. No further calls will
- // be made to other media browsing methods.
- LogHelper.w(TAG, "OnGetRoot: IGNORING request from untrusted package "
- + clientPackageName);
- return null;
- }
- if (ANDROID_AUTO_PACKAGE_NAME.equals(clientPackageName)) {
- // Optional: if your app needs to adapt ads, music library or anything else that
- // needs to run differently when connected to the car, this is where you should handle
- // it.
- }
- return new BrowserRoot(MEDIA_ID_ROOT, null);
- }
+ @Override
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ LogHelper.d(TAG, "playFromMediaId mediaId:", mediaId, " extras=", extras);
- @Override
- public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
- if (!mMusicProvider.isInitialized()) {
- // Use result.detach to allow calling result.sendResult from another thread:
- result.detach();
+ // The mediaId used here is not the unique musicId. This one comes from the
+ // MediaBrowser, and is actually a "hierarchy-aware mediaID": a concatenation of
+ // the hierarchy in MediaBrowser and the actual unique musicID. This is necessary
+ // so we can build the correct playing queue, based on where the track was
+ // selected from.
+ mPlayingQueue = QueueHelper.getPlayingQueue(mediaId, mMusicProvider);
+ mSession.setQueue(mPlayingQueue);
+ String queueTitle = getString(R.string.browse_musics_by_genre_subtitle,
+ MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaId));
+ mSession.setQueueTitle(queueTitle);
- mMusicProvider.retrieveMedia(new MusicProvider.Callback() {
- @Override
- public void onMusicCatalogReady(boolean success) {
- if (success) {
- loadChildrenImpl(parentMediaId, result);
- } else {
- updatePlaybackState(getString(R.string.error_no_metadata));
- result.sendResult(new ArrayList<MediaItem>());
- }
- }
- });
+ if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+ // set the current index on queue from the media Id:
+ mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(mPlayingQueue, mediaId);
- } else {
- // If our music catalog is already loaded/cached, load them into result immediately
- loadChildrenImpl(parentMediaId, result);
- }
- }
+ if (mCurrentIndexOnQueue < 0) {
+ LogHelper.e(TAG, "playFromMediaId: media ID ", mediaId,
+ " could not be found on queue. Ignoring.");
+ } else {
+ // play the music
+ handlePlayRequest();
+ }
+ }
+ }
- /**
- * Actual implementation of onLoadChildren that assumes that MusicProvider is already
- * initialized.
- */
- private void loadChildrenImpl(final String parentMediaId,
- final Result<List<MediaBrowser.MediaItem>> result) {
- LogHelper.d(TAG, "OnLoadChildren: parentMediaId=", parentMediaId);
+ @Override
+ public void onPause() {
+ LogHelper.d(TAG, "pause. current state=" + mPlayback.getState());
+ handlePauseRequest();
+ }
- List<MediaBrowser.MediaItem> mediaItems = new ArrayList<>();
+ @Override
+ public void onStop() {
+ LogHelper.d(TAG, "stop. current state=" + mPlayback.getState());
+ handleStopRequest(null);
+ }
- if (MEDIA_ID_ROOT.equals(parentMediaId)) {
- LogHelper.d(TAG, "OnLoadChildren.ROOT");
- mediaItems.add(new MediaBrowser.MediaItem(
- new MediaDescription.Builder()
- .setMediaId(MEDIA_ID_MUSICS_BY_GENRE)
- .setTitle(getString(R.string.browse_genres))
- .setIconUri(Uri.parse("android.resource://" +
- "com.example.android.mediabrowserservice/drawable/ic_by_genre"))
- .setSubtitle(getString(R.string.browse_genre_subtitle))
- .build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
- ));
+ @Override
+ public void onSkipToNext() {
+ LogHelper.d(TAG, "skipToNext");
+ mCurrentIndexOnQueue++;
+ if (mPlayingQueue != null && mCurrentIndexOnQueue >= mPlayingQueue.size()) {
+ // This sample's behavior: skipping to next when in last song returns to the
+ // first song.
+ mCurrentIndexOnQueue = 0;
+ }
+ if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+ handlePlayRequest();
+ } else {
+ LogHelper.e(TAG, "skipToNext: cannot skip to next. next Index=" +
+ mCurrentIndexOnQueue + " queue length=" +
+ (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
+ handleStopRequest("Cannot skip");
+ }
+ }
- } else if (MEDIA_ID_MUSICS_BY_GENRE.equals(parentMediaId)) {
- LogHelper.d(TAG, "OnLoadChildren.GENRES");
- for (String genre: mMusicProvider.getGenres()) {
- MediaBrowser.MediaItem item = new MediaBrowser.MediaItem(
- new MediaDescription.Builder()
- .setMediaId(createBrowseCategoryMediaID(MEDIA_ID_MUSICS_BY_GENRE, genre))
- .setTitle(genre)
- .setSubtitle(getString(R.string.browse_musics_by_genre_subtitle, genre))
- .build(), MediaBrowser.MediaItem.FLAG_BROWSABLE
- );
- mediaItems.add(item);
- }
+ @Override
+ public void onSkipToPrevious() {
+ LogHelper.d(TAG, "skipToPrevious");
+ mCurrentIndexOnQueue--;
+ if (mPlayingQueue != null && mCurrentIndexOnQueue < 0) {
+ // This sample's behavior: skipping to previous when in first song restarts the
+ // first song.
+ mCurrentIndexOnQueue = 0;
+ }
+ if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+ handlePlayRequest();
+ } else {
+ LogHelper.e(TAG, "skipToPrevious: cannot skip to previous. previous Index=" +
+ mCurrentIndexOnQueue + " queue length=" +
+ (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
+ handleStopRequest("Cannot skip");
+ }
+ }
- } else if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_GENRE)) {
- String genre = extractBrowseCategoryFromMediaID(parentMediaId)[1];
- LogHelper.d(TAG, "OnLoadChildren.SONGS_BY_GENRE genre=", genre);
- for (MediaMetadata track: mMusicProvider.getMusicsByGenre(genre)) {
- // Since mediaMetadata fields are immutable, we need to create a copy, so we
- // can set a hierarchy-aware mediaID. We will need to know the media hierarchy
- // when we get a onPlayFromMusicID call, so we can create the proper queue based
- // on where the music was selected from (by artist, by genre, random, etc)
- String hierarchyAwareMediaID = MediaIDHelper.createTrackMediaID(
- MEDIA_ID_MUSICS_BY_GENRE, genre, track);
- MediaMetadata trackCopy = new MediaMetadata.Builder(track)
- .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
- .build();
- MediaBrowser.MediaItem bItem = new MediaBrowser.MediaItem(
- trackCopy.getDescription(), MediaItem.FLAG_PLAYABLE);
- mediaItems.add(bItem);
- }
- } else {
- LogHelper.w(TAG, "Skipping unmatched parentMediaId: ", parentMediaId);
- }
- result.sendResult(mediaItems);
- }
+ @Override
+ public void onCustomAction(String action, Bundle extras) {
+ if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
+ LogHelper.i(TAG, "onCustomAction: favorite for current track");
+ MediaMetadata track = getCurrentPlayingMusic();
+ if (track != null) {
+ String musicId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+ mMusicProvider.setFavorite(musicId, !mMusicProvider.isFavorite(musicId));
+ }
+ // playback state needs to be updated because the "Favorite" icon on the
+ // custom action will change to reflect the new favorite state.
+ updatePlaybackState(null);
+ } else {
+ LogHelper.e(TAG, "Unsupported action: ", action);
+ }
+ }
+ @Override
+ public void onPlayFromSearch(String query, Bundle extras) {
+ LogHelper.d(TAG, "playFromSearch query=", query);
+ if (TextUtils.isEmpty(query)) {
+ // A generic search like "Play music" sends an empty query
+ // and it's expected that we start playing something. What will be played depends
+ // on the app: favorite playlist, "I'm feeling lucky", most recent, etc.
+ mPlayingQueue = QueueHelper.getRandomQueue(mMusicProvider);
+ } else {
+ mPlayingQueue = QueueHelper.getPlayingQueueFromSearch(query, mMusicProvider);
+ }
- private final class MediaSessionCallback extends MediaSession.Callback {
- @Override
- public void onPlay() {
- LogHelper.d(TAG, "play");
+ LogHelper.d(TAG, "playFromSearch playqueue.length=" + mPlayingQueue.size());
+ mSession.setQueue(mPlayingQueue);
- if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
- mPlayingQueue = QueueHelper.getRandomQueue(mMusicProvider);
- mSession.setQueue(mPlayingQueue);
- mSession.setQueueTitle(getString(R.string.random_queue_title));
- // start playing from the beginning of the queue
- mCurrentIndexOnQueue = 0;
- }
+ if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+ // immediately start playing from the beginning of the search results
+ mCurrentIndexOnQueue = 0;
- if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
- handlePlayRequest();
- }
- }
+ handlePlayRequest();
+ } else {
+ // if nothing was found, we need to warn the user and stop playing
+ handleStopRequest(getString(R.string.no_search_results));
+ }
+ }
+ }
- @Override
- public void onSkipToQueueItem(long queueId) {
- LogHelper.d(TAG, "OnSkipToQueueItem:" + queueId);
+ /**
+ * Handle a request to play music
+ */
+ private void handlePlayRequest() {
+ LogHelper.d(TAG, "handlePlayRequest: mState=" + mPlayback.getState());
- if (mState == PlaybackState.STATE_PAUSED) {
- mState = PlaybackState.STATE_STOPPED;
- }
+ mDelayedStopHandler.removeCallbacksAndMessages(null);
+ if (!mServiceStarted) {
+ LogHelper.v(TAG, "Starting service");
+ // The MusicService needs to keep running even after the calling MediaBrowser
+ // is disconnected. Call startService(Intent) and then stopSelf(..) when we no longer
+ // need to play media.
+ startService(new Intent(getApplicationContext(), MusicService.class));
+ mServiceStarted = true;
+ }
- if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
-
- // set the current index on queue from the music Id:
- mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(mPlayingQueue, queueId);
-
- // play the music
- handlePlayRequest();
- }
- }
-
- @Override
- public void onPlayFromMediaId(String mediaId, Bundle extras) {
- LogHelper.d(TAG, "playFromMediaId mediaId:", mediaId, " extras=", extras);
-
- if (mState == PlaybackState.STATE_PAUSED) {
- mState = PlaybackState.STATE_STOPPED;
- }
-
- // The mediaId used here is not the unique musicId. This one comes from the
- // MediaBrowser, and is actually a "hierarchy-aware mediaID": a concatenation of
- // the hierarchy in MediaBrowser and the actual unique musicID. This is necessary
- // so we can build the correct playing queue, based on where the track was
- // selected from.
- mPlayingQueue = QueueHelper.getPlayingQueue(mediaId, mMusicProvider);
- mSession.setQueue(mPlayingQueue);
- String queueTitle = getString(R.string.browse_musics_by_genre_subtitle,
- MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaId));
- mSession.setQueueTitle(queueTitle);
-
- if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
- String uniqueMusicID = MediaIDHelper.extractMusicIDFromMediaID(mediaId);
-
- // set the current index on queue from the music Id:
- mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(
- mPlayingQueue, uniqueMusicID);
-
- // play the music
- handlePlayRequest();
- }
- }
-
- @Override
- public void onPause() {
- LogHelper.d(TAG, "pause. current state=" + mState);
- handlePauseRequest();
- }
-
- @Override
- public void onStop() {
- LogHelper.d(TAG, "stop. current state=" + mState);
- handleStopRequest(null);
- }
-
- @Override
- public void onSkipToNext() {
- LogHelper.d(TAG, "skipToNext");
- mCurrentIndexOnQueue++;
- if (mPlayingQueue != null && mCurrentIndexOnQueue >= mPlayingQueue.size()) {
- mCurrentIndexOnQueue = 0;
- }
- if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
- mState = PlaybackState.STATE_STOPPED;
- handlePlayRequest();
- } else {
- LogHelper.e(TAG, "skipToNext: cannot skip to next. next Index=" +
- mCurrentIndexOnQueue + " queue length=" +
- (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
- handleStopRequest("Cannot skip");
- }
- }
-
- @Override
- public void onSkipToPrevious() {
- LogHelper.d(TAG, "skipToPrevious");
- mCurrentIndexOnQueue--;
- if (mPlayingQueue != null && mCurrentIndexOnQueue < 0) {
- // This sample's behavior: skipping to previous when in first song restarts the
- // first song.
- mCurrentIndexOnQueue = 0;
- }
- if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
- mState = PlaybackState.STATE_STOPPED;
- handlePlayRequest();
- } else {
- LogHelper.e(TAG, "skipToPrevious: cannot skip to previous. previous Index=" +
- mCurrentIndexOnQueue + " queue length=" +
- (mPlayingQueue == null ? "null" : mPlayingQueue.size()));
- handleStopRequest("Cannot skip");
- }
- }
-
- @Override
- public void onCustomAction(String action, Bundle extras) {
- if (CUSTOM_ACTION_THUMBS_UP.equals(action)) {
- LogHelper.i(TAG, "onCustomAction: favorite for current track");
- MediaMetadata track = getCurrentPlayingMusic();
- if (track != null) {
- String mediaId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
- mMusicProvider.setFavorite(mediaId, !mMusicProvider.isFavorite(mediaId));
- }
- updatePlaybackState(null);
- } else {
- LogHelper.e(TAG, "Unsupported action: ", action);
- }
-
- }
-
- @Override
- public void onPlayFromSearch(String query, Bundle extras) {
- LogHelper.d(TAG, "playFromSearch query=", query);
-
- if (mState == PlaybackState.STATE_PAUSED) {
- mState = PlaybackState.STATE_STOPPED;
- }
-
- mPlayingQueue = QueueHelper.getPlayingQueueFromSearch(query, mMusicProvider);
- LogHelper.d(TAG, "playFromSearch playqueue.length=" + mPlayingQueue.size());
- mSession.setQueue(mPlayingQueue);
-
- if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
- // start playing from the beginning of the queue
- mCurrentIndexOnQueue = 0;
-
- handlePlayRequest();
- }
- }
- }
-
-
-
- /*
- * Called when media player is done playing current song.
- * @see android.media.MediaPlayer.OnCompletionListener
- */
- @Override
- public void onCompletion(MediaPlayer player) {
- LogHelper.d(TAG, "onCompletion from MediaPlayer");
- // The media player finished playing the current song, so we go ahead
- // and start the next.
- if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
- // In this sample, we restart the playing queue when it gets to the end:
- mCurrentIndexOnQueue++;
- if (mCurrentIndexOnQueue >= mPlayingQueue.size()) {
- mCurrentIndexOnQueue = 0;
- }
- handlePlayRequest();
- } else {
- // If there is nothing to play, we stop and release the resources:
- handleStopRequest(null);
- }
- }
-
- /*
- * Called when media player is done preparing.
- * @see android.media.MediaPlayer.OnPreparedListener
- */
- @Override
- public void onPrepared(MediaPlayer player) {
- LogHelper.d(TAG, "onPrepared from MediaPlayer");
- // The media player is done preparing. That means we can start playing if we
- // have audio focus.
- configMediaPlayerState();
- }
-
- /**
- * Called when there's an error playing media. When this happens, the media
- * player goes to the Error state. We warn the user about the error and
- * reset the media player.
- *
- * @see android.media.MediaPlayer.OnErrorListener
- */
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- LogHelper.e(TAG, "Media player error: what=" + what + ", extra=" + extra);
- handleStopRequest("MediaPlayer error " + what + " (" + extra + ")");
- return true; // true indicates we handled the error
- }
-
-
-
-
- /**
- * Called by AudioManager on audio focus changes.
- */
- @Override
- public void onAudioFocusChange(int focusChange) {
- LogHelper.d(TAG, "onAudioFocusChange. focusChange=" + focusChange);
- if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
- // We have gained focus:
- mAudioFocus = AudioFocus.Focused;
-
- } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS ||
- focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
- focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
- // We have lost focus. If we can duck (low playback volume), we can keep playing.
- // Otherwise, we need to pause the playback.
- boolean canDuck = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
- mAudioFocus = canDuck ? AudioFocus.NoFocusCanDuck : AudioFocus.NoFocusNoDuck;
-
- // If we are playing, we need to reset media player by calling configMediaPlayerState
- // with mAudioFocus properly set.
- if (mState == PlaybackState.STATE_PLAYING && !canDuck) {
- // If we don't have audio focus and can't duck, we save the information that
- // we were playing, so that we can resume playback once we get the focus back.
- mPlayOnFocusGain = true;
- }
- } else {
- LogHelper.e(TAG, "onAudioFocusChange: Ignoring unsupported focusChange: " + focusChange);
- }
-
- configMediaPlayerState();
- }
-
-
-
- /**
- * Handle a request to play music
- */
- private void handlePlayRequest() {
- LogHelper.d(TAG, "handlePlayRequest: mState=" + mState);
-
- mDelayedStopHandler.removeCallbacksAndMessages(null);
- if (!mServiceStarted) {
- LogHelper.v(TAG, "Starting service");
- // The MusicService needs to keep running even after the calling MediaBrowser
- // is disconnected. Call startService(Intent) and then stopSelf(..) when we no longer
- // need to play media.
- startService(new Intent(getApplicationContext(), MusicService.class));
- mServiceStarted = true;
- }
-
- mPlayOnFocusGain = true;
- tryToGetAudioFocus();
-
- if (!mSession.isActive()) {
- mSession.setActive(true);
- }
-
- // actually play the song
- if (mState == PlaybackState.STATE_PAUSED) {
- // If we're paused, just continue playback and restore the
- // 'foreground service' state.
- configMediaPlayerState();
- } else {
- // If we're stopped or playing a song,
- // just go ahead to the new song and (re)start playing
- playCurrentSong();
- }
- }
-
-
- /**
- * Handle a request to pause music
- */
- private void handlePauseRequest() {
- LogHelper.d(TAG, "handlePauseRequest: mState=" + mState);
-
- if (mState == PlaybackState.STATE_PLAYING) {
- // Pause media player and cancel the 'foreground service' state.
- mState = PlaybackState.STATE_PAUSED;
- if (mMediaPlayer.isPlaying()) {
- mMediaPlayer.pause();
- }
- // while paused, retain the MediaPlayer but give up audio focus
- relaxResources(false);
- giveUpAudioFocus();
- }
- updatePlaybackState(null);
- }
-
- /**
- * Handle a request to stop music
- */
- private void handleStopRequest(String withError) {
- LogHelper.d(TAG, "handleStopRequest: mState=" + mState + " error=", withError);
- mState = PlaybackState.STATE_STOPPED;
-
- // let go of all resources...
- relaxResources(true);
- giveUpAudioFocus();
- updatePlaybackState(withError);
-
- mMediaNotification.stopNotification();
-
- // service is no longer necessary. Will be started again if needed.
- stopSelf();
- mServiceStarted = false;
- }
-
- /**
- * Releases resources used by the service for playback. This includes the
- * "foreground service" status, the wake locks and possibly the MediaPlayer.
- *
- * @param releaseMediaPlayer Indicates whether the Media Player should also
- * be released or not
- */
- private void relaxResources(boolean releaseMediaPlayer) {
- LogHelper.d(TAG, "relaxResources. releaseMediaPlayer=" + releaseMediaPlayer);
- // stop being a foreground service
- stopForeground(true);
-
- // reset the delayed stop handler.
- mDelayedStopHandler.removeCallbacksAndMessages(null);
- mDelayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY);
-
- // stop and release the Media Player, if it's available
- if (releaseMediaPlayer && mMediaPlayer != null) {
- mMediaPlayer.reset();
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
-
- // we can also release the Wifi lock, if we're holding it
- if (mWifiLock.isHeld()) {
- mWifiLock.release();
- }
- }
-
- /**
- * Reconfigures MediaPlayer according to audio focus settings and
- * starts/restarts it. This method starts/restarts the MediaPlayer
- * respecting the current audio focus state. So if we have focus, it will
- * play normally; if we don't have focus, it will either leave the
- * MediaPlayer paused or set it to a low volume, depending on what is
- * allowed by the current focus settings. This method assumes mPlayer !=
- * null, so if you are calling it, you have to do so from a context where
- * you are sure this is the case.
- */
- private void configMediaPlayerState() {
- LogHelper.d(TAG, "configAndStartMediaPlayer. mAudioFocus=" + mAudioFocus);
- if (mAudioFocus == AudioFocus.NoFocusNoDuck) {
- // If we don't have audio focus and can't duck, we have to pause,
- if (mState == PlaybackState.STATE_PLAYING) {
- handlePauseRequest();
- }
- } else { // we have audio focus:
- if (mAudioFocus == AudioFocus.NoFocusCanDuck) {
- mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK); // we'll be relatively quiet
- } else {
- mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL); // we can be loud again
- }
- // If we were playing when we lost focus, we need to resume playing.
- if (mPlayOnFocusGain) {
- if (!mMediaPlayer.isPlaying()) {
- LogHelper.d(TAG, "configAndStartMediaPlayer startMediaPlayer.");
- mMediaPlayer.start();
- }
- mPlayOnFocusGain = false;
- mState = PlaybackState.STATE_PLAYING;
- }
- }
- updatePlaybackState(null);
- }
+ if (!mSession.isActive()) {
+ mSession.setActive(true);
+ }
- /**
- * Makes sure the media player exists and has been reset. This will create
- * the media player if needed, or reset the existing media player if one
- * already exists.
- */
- private void createMediaPlayerIfNeeded() {
- LogHelper.d(TAG, "createMediaPlayerIfNeeded. needed? " + (mMediaPlayer==null));
- if (mMediaPlayer == null) {
- mMediaPlayer = new MediaPlayer();
+ if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+ updateMetadata();
+ mPlayback.play(mPlayingQueue.get(mCurrentIndexOnQueue));
+ }
+ }
- // Make sure the media player will acquire a wake-lock while
- // playing. If we don't do that, the CPU might go to sleep while the
- // song is playing, causing playback to stop.
- mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
+ /**
+ * Handle a request to pause music
+ */
+ private void handlePauseRequest() {
+ LogHelper.d(TAG, "handlePauseRequest: mState=" + mPlayback.getState());
+ mPlayback.pause();
+ // reset the delayed stop handler.
+ mDelayedStopHandler.removeCallbacksAndMessages(null);
+ mDelayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY);
+ }
- // we want the media player to notify us when it's ready preparing,
- // and when it's done playing:
- mMediaPlayer.setOnPreparedListener(this);
- mMediaPlayer.setOnCompletionListener(this);
- mMediaPlayer.setOnErrorListener(this);
- } else {
- mMediaPlayer.reset();
- }
- }
+ /**
+ * Handle a request to stop music
+ */
+ private void handleStopRequest(String withError) {
+ LogHelper.d(TAG, "handleStopRequest: mState=" + mPlayback.getState() + " error=", withError);
+ mPlayback.stop(true);
+ // reset the delayed stop handler.
+ mDelayedStopHandler.removeCallbacksAndMessages(null);
+ mDelayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY);
- /**
- * Starts playing the current song in the playing queue.
- */
- void playCurrentSong() {
- MediaMetadata track = getCurrentPlayingMusic();
- if (track == null) {
- LogHelper.e(TAG, "playSong: ignoring request to play next song, because cannot" +
- " find it." +
- " currentIndex=" + mCurrentIndexOnQueue +
- " playQueue.size=" + (mPlayingQueue==null?"null": mPlayingQueue.size()));
- return;
- }
- String source = track.getString(MusicProvider.CUSTOM_METADATA_TRACK_SOURCE);
- LogHelper.d(TAG, "playSong: current (" + mCurrentIndexOnQueue + ") in playingQueue. " +
- " musicId=" + track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID) +
- " source=" + source);
+ updatePlaybackState(withError);
- mState = PlaybackState.STATE_STOPPED;
- relaxResources(false); // release everything except MediaPlayer
+ // service is no longer necessary. Will be started again if needed.
+ stopSelf();
+ mServiceStarted = false;
+ }
- try {
- createMediaPlayerIfNeeded();
+ private void updateMetadata() {
+ if (!QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+ LogHelper.e(TAG, "Can't retrieve current metadata.");
+ updatePlaybackState(getResources().getString(R.string.error_no_metadata));
+ return;
+ }
+ MediaSession.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue);
+ String musicId = MediaIDHelper.extractMusicIDFromMediaID(
+ queueItem.getDescription().getMediaId());
+ MediaMetadata track = mMusicProvider.getMusic(musicId);
+ final String trackId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+ if (!musicId.equals(trackId)) {
+ IllegalStateException e = new IllegalStateException("track ID should match musicId.");
+ LogHelper.e(TAG, "track ID should match musicId.",
+ " musicId=", musicId, " trackId=", trackId,
+ " mediaId from queueItem=", queueItem.getDescription().getMediaId(),
+ " title from queueItem=", queueItem.getDescription().getTitle(),
+ " mediaId from track=", track.getDescription().getMediaId(),
+ " title from track=", track.getDescription().getTitle(),
+ " source.hashcode from track=", track.getString(
+ MusicProvider.CUSTOM_METADATA_TRACK_SOURCE).hashCode(),
+ e);
+ throw e;
+ }
+ LogHelper.d(TAG, "Updating metadata for MusicID= " + musicId);
+ mSession.setMetadata(track);
- mState = PlaybackState.STATE_BUFFERING;
+ // Set the proper album artwork on the media session, so it can be shown in the
+ // locked screen and in other places.
+ if (track.getDescription().getIconBitmap() == null &&
+ track.getDescription().getIconUri() != null) {
+ String albumUri = track.getDescription().getIconUri().toString();
+ AlbumArtCache.getInstance().fetch(albumUri, new AlbumArtCache.FetchListener() {
+ @Override
+ public void onFetched(String artUrl, Bitmap bitmap, Bitmap icon) {
+ MediaSession.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue);
+ MediaMetadata track = mMusicProvider.getMusic(trackId);
+ track = new MediaMetadata.Builder(track)
- mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- mMediaPlayer.setDataSource(source);
+ // set high resolution bitmap in METADATA_KEY_ALBUM_ART. This is used, for
+ // example, on the lockscreen background when the media session is active.
+ .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap)
- // Starts preparing the media player in the background. When
- // it's done, it will call our OnPreparedListener (that is,
- // the onPrepared() method on this class, since we set the
- // listener to 'this'). Until the media player is prepared,
- // we *cannot* call start() on it!
- mMediaPlayer.prepareAsync();
+ // set small version of the album art in the DISPLAY_ICON. This is used on
+ // the MediaDescription and thus it should be small to be serialized if
+ // necessary..
+ .putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon)
- // If we are streaming from the internet, we want to hold a
- // Wifi lock, which prevents the Wifi radio from going to
- // sleep while the song is playing.
- mWifiLock.acquire();
+ .build();
- updatePlaybackState(null);
- updateMetadata();
+ mMusicProvider.updateMusic(trackId, track);
- } catch (IOException ex) {
- LogHelper.e(TAG, ex, "IOException playing song");
- updatePlaybackState(ex.getMessage());
- }
- }
+ // If we are still playing the same music
+ String currentPlayingId = MediaIDHelper.extractMusicIDFromMediaID(
+ queueItem.getDescription().getMediaId());
+ if (trackId.equals(currentPlayingId)) {
+ mSession.setMetadata(track);
+ }
+ }
+ });
+ }
+ }
+ /**
+ * Update the current media player state, optionally showing an error message.
+ *
+ * @param error if not null, error message to present to the user.
+ */
+ private void updatePlaybackState(String error) {
+ LogHelper.d(TAG, "updatePlaybackState, playback state=" + mPlayback.getState());
+ long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
+ if (mPlayback != null && mPlayback.isConnected()) {
+ position = mPlayback.getCurrentStreamPosition();
+ }
+ PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
+ .setActions(getAvailableActions());
- private void updateMetadata() {
- if (!QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
- LogHelper.e(TAG, "Can't retrieve current metadata.");
- mState = PlaybackState.STATE_ERROR;
- updatePlaybackState(getResources().getString(R.string.error_no_metadata));
- return;
- }
- MediaSession.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue);
- String mediaId = queueItem.getDescription().getMediaId();
- MediaMetadata track = mMusicProvider.getMusic(mediaId);
- String trackId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
- if (!mediaId.equals(trackId)) {
- throw new IllegalStateException("track ID (" + trackId + ") " +
- "should match mediaId (" + mediaId + ")");
- }
- LogHelper.d(TAG, "Updating metadata for MusicID= " + mediaId);
- mSession.setMetadata(track);
- }
+ setCustomAction(stateBuilder);
+ int state = mPlayback.getState();
+ // If there is an error message, send it to the playback state:
+ if (error != null) {
+ // Error states are really only supposed to be used for errors that cause playback to
+ // stop unexpectedly and persist until the user takes action to fix it.
+ stateBuilder.setErrorMessage(error);
+ state = PlaybackState.STATE_ERROR;
+ }
+ stateBuilder.setState(state, position, 1.0f, SystemClock.elapsedRealtime());
- /**
- * Update the current media player state, optionally showing an error message.
- *
- * @param error if not null, error message to present to the user.
- *
- */
- private void updatePlaybackState(String error) {
+ // Set the activeQueueItemId if the current index is valid.
+ if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+ MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
+ stateBuilder.setActiveQueueItemId(item.getQueueId());
+ }
- LogHelper.d(TAG, "updatePlaybackState, setting session playback state to " + mState);
- long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
- if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- position = mMediaPlayer.getCurrentPosition();
- }
- PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
- .setActions(getAvailableActions());
+ mSession.setPlaybackState(stateBuilder.build());
- setCustomAction(stateBuilder);
+ if (state == PlaybackState.STATE_PLAYING || state == PlaybackState.STATE_PAUSED) {
+ mMediaNotificationManager.startNotification();
+ }
+ }
- // If there is an error message, send it to the playback state:
- if (error != null) {
- // Error states are really only supposed to be used for errors that cause playback to
- // stop unexpectedly and persist until the user takes action to fix it.
- stateBuilder.setErrorMessage(error);
- mState = PlaybackState.STATE_ERROR;
- }
- stateBuilder.setState(mState, position, 1.0f, SystemClock.elapsedRealtime());
+ private void setCustomAction(PlaybackState.Builder stateBuilder) {
+ MediaMetadata currentMusic = getCurrentPlayingMusic();
+ if (currentMusic != null) {
+ // Set appropriate "Favorite" icon on Custom action:
+ String musicId = currentMusic.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+ int favoriteIcon = R.drawable.ic_star_off;
+ if (mMusicProvider.isFavorite(musicId)) {
+ favoriteIcon = R.drawable.ic_star_on;
+ }
+ LogHelper.d(TAG, "updatePlaybackState, setting Favorite custom action of music ",
+ musicId, " current favorite=", mMusicProvider.isFavorite(musicId));
+ stateBuilder.addCustomAction(CUSTOM_ACTION_THUMBS_UP, getString(R.string.favorite),
+ favoriteIcon);
+ }
+ }
- // Set the activeQueueItemId if the current index is valid.
- if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
- MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
- stateBuilder.setActiveQueueItemId(item.getQueueId());
- }
+ private long getAvailableActions() {
+ long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
+ PlaybackState.ACTION_PLAY_FROM_SEARCH;
+ if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
+ return actions;
+ }
+ if (mPlayback.isPlaying()) {
+ actions |= PlaybackState.ACTION_PAUSE;
+ }
+ if (mCurrentIndexOnQueue > 0) {
+ actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+ }
+ if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) {
+ actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
+ }
+ return actions;
+ }
- mSession.setPlaybackState(stateBuilder.build());
+ private MediaMetadata getCurrentPlayingMusic() {
+ if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
+ MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
+ if (item != null) {
+ LogHelper.d(TAG, "getCurrentPlayingMusic for musicId=",
+ item.getDescription().getMediaId());
+ return mMusicProvider.getMusic(
+ MediaIDHelper.extractMusicIDFromMediaID(item.getDescription().getMediaId()));
+ }
+ }
+ return null;
+ }
- if (mState == PlaybackState.STATE_PLAYING || mState == PlaybackState.STATE_PAUSED) {
- mMediaNotification.startNotification();
- }
- }
+ /**
+ * Implementation of the Playback.Callback interface
+ */
+ @Override
+ public void onCompletion() {
+ // The media player finished playing the current song, so we go ahead
+ // and start the next.
+ if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) {
+ // In this sample, we restart the playing queue when it gets to the end:
+ mCurrentIndexOnQueue++;
+ if (mCurrentIndexOnQueue >= mPlayingQueue.size()) {
+ mCurrentIndexOnQueue = 0;
+ }
+ handlePlayRequest();
+ } else {
+ // If there is nothing to play, we stop and release the resources:
+ handleStopRequest(null);
+ }
+ }
- private void setCustomAction(PlaybackState.Builder stateBuilder) {
- MediaMetadata currentMusic = getCurrentPlayingMusic();
- if (currentMusic != null) {
- // Set appropriate "Favorite" icon on Custom action:
- String mediaId = currentMusic.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
- int favoriteIcon = R.drawable.ic_star_off;
- if (mMusicProvider.isFavorite(mediaId)) {
- favoriteIcon = R.drawable.ic_star_on;
- }
- LogHelper.d(TAG, "updatePlaybackState, setting Favorite custom action of music ",
- mediaId, " current favorite=", mMusicProvider.isFavorite(mediaId));
- stateBuilder.addCustomAction(CUSTOM_ACTION_THUMBS_UP, getString(R.string.favorite),
- favoriteIcon);
- }
- }
+ @Override
+ public void onPlaybackStatusChanged(int state) {
+ updatePlaybackState(null);
+ }
- private long getAvailableActions() {
- long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID |
- PlaybackState.ACTION_PLAY_FROM_SEARCH;
- if (mPlayingQueue == null || mPlayingQueue.isEmpty()) {
- return actions;
- }
- if (mState == PlaybackState.STATE_PLAYING) {
- actions |= PlaybackState.ACTION_PAUSE;
- }
- if (mCurrentIndexOnQueue > 0) {
- actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS;
- }
- if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) {
- actions |= PlaybackState.ACTION_SKIP_TO_NEXT;
- }
- return actions;
- }
+ @Override
+ public void onError(String error) {
+ updatePlaybackState(error);
+ }
- private MediaMetadata getCurrentPlayingMusic() {
- if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) {
- MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue);
- if (item != null) {
- LogHelper.d(TAG, "getCurrentPlayingMusic for musicId=",
- item.getDescription().getMediaId());
- return mMusicProvider.getMusic(item.getDescription().getMediaId());
- }
- }
- return null;
- }
+ /**
+ * A simple handler that stops the service if playback is not active (playing)
+ */
+ private static class DelayedStopHandler extends Handler {
+ private final WeakReference<MusicService> mWeakReference;
- /**
- * Try to get the system audio focus.
- */
- void tryToGetAudioFocus() {
- LogHelper.d(TAG, "tryToGetAudioFocus");
- if (mAudioFocus != AudioFocus.Focused) {
- int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
- if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocus = AudioFocus.Focused;
- }
- }
- }
+ private DelayedStopHandler(MusicService service) {
+ mWeakReference = new WeakReference<>(service);
+ }
- /**
- * Give up the audio focus.
- */
- void giveUpAudioFocus() {
- LogHelper.d(TAG, "giveUpAudioFocus");
- if (mAudioFocus == AudioFocus.Focused) {
- if (mAudioManager.abandonAudioFocus(this) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- mAudioFocus = AudioFocus.NoFocusNoDuck;
- }
- }
- }
-}
+ @Override
+ public void handleMessage(Message msg) {
+ MusicService service = mWeakReference.get();
+ if (service != null && service.mPlayback != null) {
+ if (service.mPlayback.isPlaying()) {
+ LogHelper.d(TAG, "Ignoring delayed stop since the media player is in use.");
+ return;
+ }
+ LogHelper.d(TAG, "Stopping service with delay handler.");
+ service.stopSelf();
+ service.mServiceStarted = false;
+ }
+ }
+ }
+ }
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/PackageValidator.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/PackageValidator.java
index 090b6a4..c2bce54 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/PackageValidator.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/PackageValidator.java
@@ -15,202 +15,145 @@
*/
package com.example.android.mediabrowserservice;
-import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.os.Build;
+import android.content.res.XmlResourceParser;
import android.os.Process;
import android.util.Base64;
-import android.util.Log;
+
+import com.example.android.mediabrowserservice.utils.LogHelper;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
/**
- * Validates that the calling package is authorized to use this
+ * Validates that the calling package is authorized to browse a
* {@link android.service.media.MediaBrowserService}.
+ *
+ * The list of allowed signing certificates and their corresponding package names is defined in
+ * res/xml/allowed_media_browser_callers.xml.
*/
public class PackageValidator {
- public static final String TAG = "PackageValidator";
-
- // Replace with your package whitelist
- static final byte[][] VALID_PUBLIC_SIGNATURES = new byte[][]{
- // Android Auto release public key
- extractKey(
- "\060\202\003\275\060\202\002\245\240\003\002\001\002\002\011\000\307\217\236\113" +
- "\223\101\060\006\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060" +
- "\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061\023\060\021\006\003" +
- "\125\004\010\014\012\103\141\154\151\146\157\162\156\151\141\061\026\060\024\006" +
- "\003\125\004\007\014\015\115\157\165\156\164\141\151\156\040\126\151\145\167\061" +
- "\024\060\022\006\003\125\004\012\014\013\107\157\157\147\154\145\040\111\156\143" +
- "\056\061\020\060\016\006\003\125\004\013\014\007\101\156\144\162\157\151\144\061" +
- "\021\060\017\006\003\125\004\003\014\010\147\145\141\162\150\145\141\144\060\036" +
- "\027\015\061\064\060\065\062\067\062\063\060\065\063\064\132\027\015\064\061\061" +
- "\060\061\062\062\063\060\065\063\064\132\060\165\061\013\060\011\006\003\125\004" +
- "\006\023\002\125\123\061\023\060\021\006\003\125\004\010\014\012\103\141\154\151" +
- "\146\157\162\156\151\141\061\026\060\024\006\003\125\004\007\014\015\115\157\165" +
- "\156\164\141\151\156\040\126\151\145\167\061\024\060\022\006\003\125\004\012\014" +
- "\013\107\157\157\147\154\145\040\111\156\143\056\061\020\060\016\006\003\125\004" +
- "\013\014\007\101\156\144\162\157\151\144\061\021\060\017\006\003\125\004\003\014" +
- "\010\147\145\141\162\150\145\141\144\060\202\001\042\060\015\006\011\052\206\110" +
- "\206\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002\202\001" +
- "\001\000\323\235\027\016\103\110\261\124\114\137\154\023\275\132\145\244\053\270" +
- "\072\331\362\064\255\257\344\036\317\113\340\340\202\141\366\312\346\142\302\224" +
- "\356\255\322\203\103\324\175\123\074\107\365\116\045\260\057\246\043\025\344\210" +
- "\026\012\041\143\125\200\313\142\116\014\144\023\056\334\201\153\335\140\170\015" +
- "\142\221\156\360\214\131\051\200\362\135\353\076\323\152\137\276\233\272\334\302" +
- "\001\017\363\347\275\121\142\246\215\150\122\266\337\172\330\376\232\272\004\246" +
- "\071\300\357\130\024\113\103\244\370\176\227\131\153\046\157\314\105\035\005\114" +
- "\241\225\204\043\073\024\047\151\341\233\301\034\234\371\000\075\363\131\000\157" +
- "\276\134\263\321\072\204\120\011\253\060\311\213\035\343\142\156\140\003\367\013" +
- "\006\156\204\067\024\154\305\246\223\272\301\213\320\125\103\310\046\222\266\360" +
- "\252\217\170\003\272\222\264\265\051\334\334\202\232\122\222\130\166\231\323\224" +
- "\254\244\103\360\261\367\055\221\255\050\134\156\133\206\004\372\353\261\014\013" +
- "\064\076\142\301\115\326\202\121\057\264\052\372\143\020\214\122\154\337\002\003" +
- "\001\000\001\243\120\060\116\060\035\006\003\125\035\016\004\026\004\024\032\360" +
- "\137\140\327\256\350\224\211\122\162\131\012\046\201\032\311\327\316\333\060\037" +
- "\006\003\125\035\043\004\030\060\026\200\024\032\360\137\140\327\256\350\224\211" +
- "\122\162\131\012\046\201\032\311\327\316\333\060\014\006\003\125\035\023\004\005" +
- "\060\003\001\001\377\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000" +
- "\003\202\001\001\000\224\153\003\143\101\017\273\163\101\110\176\144\352\054\077" +
- "\300\230\175\173\174\114\301\055\173\022\262\206\226\034\226\242\014\111\063\062" +
- "\343\000\336\240\321\240\217\037\020\170\320\204\002\373\312\200\227\344\113\355" +
- "\124\061\352\214\155\265\375\046\337\134\224\031\003\334\065\206\355\330\054\101" +
- "\114\040\053\363\316\150\054\256\155\331\060\042\346\324\063\205\336\231\021\210" +
- "\241\131\045\026\121\337\327\360\024\021\242\354\133\242\313\075\101\260\100\376" +
- "\042\061\320\352\103\153\030\200\162\256\302\157\256\323\205\345\331\017\021\256" +
- "\103\307\346\035\206\313\307\316\051\022\371\267\015\003\201\374\262\014\222\112" +
- "\120\111\361\002\325\377\250\077\134\301\336\352\317\123\367\122\274\100\377\054" +
- "\050\016\166\272\161\147\227\142\355\054\022\312\347\276\126\257\323\145\014\267" +
- "\342\323\362\200\114\303\331\337\041\026\130\177\311\370\126\220\310\263\071\342" +
- "\027\161\254\225\001\007\115\237\234\351\006\113\232\313\133\044\030\350\320\103" +
- "\231\023\154\067\003\316\050\016\331\035\253\252\176\207\011\337\145\345\235\026" +
- "\041"),
-
- // Android Auto debug public key
- extractKey(
- "\060\202\003\275\060\202\002\245\240\003\002\001\002\002\011\000\347\344\006\360" +
- "\327\303\226\363\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060" +
- "\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061\023\060\021\006\003" +
- "\125\004\010\014\012\103\141\154\151\146\157\162\156\151\141\061\026\060\024\006" +
- "\003\125\004\007\014\015\115\157\165\156\164\141\151\156\040\126\151\145\167\061" +
- "\024\060\022\006\003\125\004\012\014\013\107\157\157\147\154\145\040\111\156\143" +
- "\056\061\020\060\016\006\003\125\004\013\014\007\101\156\144\162\157\151\144\061" +
- "\021\060\017\006\003\125\004\003\014\010\147\145\141\162\150\145\141\144\060\036" +
- "\027\015\061\064\060\065\062\067\062\063\060\062\065\061\132\027\015\064\061\061" +
- "\060\061\062\062\063\060\062\065\061\132\060\165\061\013\060\011\006\003\125\004" +
- "\006\023\002\125\123\061\023\060\021\006\003\125\004\010\014\012\103\141\154\151" +
- "\146\157\162\156\151\141\061\026\060\024\006\003\125\004\007\014\015\115\157\165" +
- "\156\164\141\151\156\040\126\151\145\167\061\024\060\022\006\003\125\004\012\014" +
- "\013\107\157\157\147\154\145\040\111\156\143\056\061\020\060\016\006\003\125\004" +
- "\013\014\007\101\156\144\162\157\151\144\061\021\060\017\006\003\125\004\003\014" +
- "\010\147\145\141\162\150\145\141\144\060\202\001\042\060\015\006\011\052\206\110" +
- "\206\367\015\001\001\001\005\000\003\202\001\017\000\060\202\001\012\002\202\001" +
- "\001\000\242\356\360\300\022\205\313\071\352\245\032\336\264\235\304\126\236\171" +
- "\375\212\364\343\320\040\347\011\106\276\260\247\214\203\374\016\263\053\123\353" +
- "\044\174\247\265\016\154\051\260\263\155\236\030\142\064\177\211\323\115\013\242" +
- "\115\341\163\310\335\130\247\212\072\212\163\050\140\315\274\277\307\276\164\273" +
- "\321\234\244\333\250\043\366\073\114\060\174\375\331\246\135\246\154\003\353\261" +
- "\115\231\071\106\330\121\021\257\344\360\060\076\132\201\243\347\260\124\166\316" +
- "\126\272\272\005\057\034\154\363\353\226\003\306\220\231\261\017\323\243\014\203" +
- "\056\174\140\061\250\057\206\364\276\071\354\167\312\035\205\067\272\111\177\004" +
- "\264\334\247\106\166\105\217\154\272\237\364\127\246\323\333\071\216\067\231\133" +
- "\363\267\106\011\312\241\023\310\047\204\013\053\275\036\176\060\031\250\234\201" +
- "\031\300\331\311\003\060\072\317\274\034\211\047\255\247\374\371\304\131\044\074" +
- "\352\073\036\353\266\331\174\063\162\206\007\141\005\226\064\351\353\361\162\304" +
- "\222\347\002\216\220\225\171\373\032\266\032\225\062\064\310\265\075\165\002\003" +
- "\001\000\001\243\120\060\116\060\035\006\003\125\035\016\004\026\004\024\365\003" +
- "\311\347\022\104\014\017\014\015\003\053\217\110\146\333\360\066\005\031\060\037" +
- "\006\003\125\035\043\004\030\060\026\200\024\365\003\311\347\022\104\014\017\014" +
- "\015\003\053\217\110\146\333\360\066\005\031\060\014\006\003\125\035\023\004\005" +
- "\060\003\001\001\377\060\015\006\011\052\206\110\206\367\015\001\001\005\005\000" +
- "\003\202\001\001\000\015\312\371\207\121\121\360\212\146\067\210\122\261\100\075" +
- "\112\160\220\127\045\332\324\144\041\316\224\040\105\261\176\236\231\040\072\175" +
- "\214\171\272\174\155\335\274\126\227\340\242\200\366\070\023\120\134\045\034\146" +
- "\111\373\245\150\376\372\353\175\036\023\233\035\126\225\344\123\140\322\227\103" +
- "\250\271\332\365\006\175\143\212\022\371\232\342\214\256\364\135\237\304\216\126" +
- "\024\036\370\156\322\222\043\144\006\303\360\051\202\026\132\060\111\036\171\250" +
- "\044\243\063\230\222\337\262\331\007\175\222\062\275\101\006\046\053\064\013\347" +
- "\160\250\330\101\122\274\162\324\321\316\032\115\101\003\301\201\160\100\367\305" +
- "\345\371\335\103\077\055\064\045\144\056\027\113\054\232\022\234\046\353\337\164" +
- "\111\305\027\261\357\153\034\377\200\044\075\237\066\253\100\215\302\044\037\035" +
- "\071\165\160\027\311\234\310\064\101\317\202\121\371\200\351\136\216\201\017\347" +
- "\306\267\136\150\277\354\346\250\057\061\151\077\117\327\362\140\240\065\342\062" +
- "\034\277\352\274\040\166\057\126\304\367\374\231\276\323\234\020\276\012\113\027" +
- "\320"),
- };
+ private static final String TAG = LogHelper.makeLogTag(PackageValidator.class);
/**
- * Disallow instantiation of this helper class.
+ * Map allowed callers' certificate keys to the expected caller information.
+ *
*/
- private PackageValidator() {}
+ private final Map<String, ArrayList<CallerInfo>> mValidCertificates;
- /**
- * Throws when the caller is not authorized to get data from this MediaBrowserService
- */
- public static void checkCallerAllowed(Context context, String callingPackage, int callingUid) {
- if (!isCallerAllowed(context, callingPackage, callingUid)) {
- throw new SecurityException("signature check failed.");
+ public PackageValidator(Context ctx) {
+ mValidCertificates = readValidCertificates(ctx.getResources().getXml(
+ R.xml.allowed_media_browser_callers));
+ }
+
+ private Map<String, ArrayList<CallerInfo>> readValidCertificates(XmlResourceParser parser) {
+ HashMap<String, ArrayList<CallerInfo>> validCertificates = new HashMap<>();
+ try {
+ int eventType = parser.next();
+ while (eventType != XmlResourceParser.END_DOCUMENT) {
+ if (eventType == XmlResourceParser.START_TAG
+ && parser.getName().equals("signing_certificate")) {
+
+ String name = parser.getAttributeValue(null, "name");
+ String packageName = parser.getAttributeValue(null, "package");
+ boolean isRelease = parser.getAttributeBooleanValue(null, "release", false);
+ String certificate = parser.nextText().replaceAll("\\s|\\n", "");
+
+ CallerInfo info = new CallerInfo(name, packageName, isRelease, certificate);
+
+ ArrayList<CallerInfo> infos = validCertificates.get(certificate);
+ if (infos == null) {
+ infos = new ArrayList<>();
+ validCertificates.put(certificate, infos);
+ }
+ LogHelper.v(TAG, "Adding allowed caller: ", info.name,
+ " package=", info.packageName, " release=", info.release,
+ " certificate=", certificate);
+ infos.add(info);
+ }
+ eventType = parser.next();
+ }
+ } catch (XmlPullParserException | IOException e) {
+ LogHelper.e(TAG, e, "Could not read allowed callers from XML.");
}
+ return validCertificates;
}
/**
* @return false if the caller is not authorized to get data from this MediaBrowserService
*/
- public static boolean isCallerAllowed(Context context, String callingPackage, int callingUid) {
- // Always allow calls from the framework or development environment.
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ public boolean isCallerAllowed(Context context, String callingPackage, int callingUid) {
+ // Always allow calls from the framework, self app or development environment.
if (Process.SYSTEM_UID == callingUid || Process.myUid() == callingUid) {
return true;
}
- if (BuildConfig.DEBUG) {
- // When your app is built in debug mode, any app is allowed to connect to it and browse
- // its media library. If you want to test the behavior of your app when it gets
- // released, either build a release version or remove this clause.
- Log.i(TAG, "Allowing caller '"+callingPackage+" because app was built in debug mode.");
- return true;
- }
+ PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
- final PackageManager packageManager = context.getPackageManager();
try {
packageInfo = packageManager.getPackageInfo(
callingPackage, PackageManager.GET_SIGNATURES);
- } catch (PackageManager.NameNotFoundException ignored) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Package manager can't find package " + callingPackage
- + ", defaulting to false");
+ } catch (PackageManager.NameNotFoundException e) {
+ LogHelper.w(TAG, e, "Package manager can't find package: ", callingPackage);
+ return false;
+ }
+ if (packageInfo.signatures.length != 1) {
+ LogHelper.w(TAG, "Caller has more than one signature certificate!");
+ return false;
+ }
+ String signature = Base64.encodeToString(
+ packageInfo.signatures[0].toByteArray(), Base64.NO_WRAP);
+
+ // Test for known signatures:
+ ArrayList<CallerInfo> validCallers = mValidCertificates.get(signature);
+ if (validCallers == null) {
+ LogHelper.v(TAG, "Signature for caller ", callingPackage, " is not valid: \n"
+ , signature);
+ if (mValidCertificates.isEmpty()) {
+ LogHelper.w(TAG, "The list of valid certificates is empty. Either your file ",
+ "res/xml/allowed_media_browser_callers.xml is empty or there was an error ",
+ "while reading it. Check previous log messages.");
}
return false;
}
- if (packageInfo == null) {
- Log.w(TAG, "Package manager can't find package: " + callingPackage);
- return false;
- }
- if (packageInfo.signatures.length != 1) {
- Log.w(TAG, "Package has more than one signature.");
- return false;
- }
- final byte[] signature = packageInfo.signatures[0].toByteArray();
-
- for (int i = 0; i < VALID_PUBLIC_SIGNATURES.length; i++) {
- byte[] validSignature = VALID_PUBLIC_SIGNATURES[i];
- if (Arrays.equals(validSignature, signature)) {
+ // Check if the package name is valid for the certificate:
+ StringBuffer expectedPackages = new StringBuffer();
+ for (CallerInfo info: validCallers) {
+ if (callingPackage.equals(info.packageName)) {
+ LogHelper.v(TAG, "Valid caller: ", info.name, " package=", info.packageName,
+ " release=", info.release);
return true;
}
+ expectedPackages.append(info.packageName).append(' ');
}
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Signature not valid. Found: \n" +
- Base64.encodeToString(signature, 0));
- }
+ LogHelper.i(TAG, "Caller has a valid certificate, but its package doesn't match any ",
+ "expected package for the given certificate. Caller's package is ", callingPackage,
+ ". Expected packages as defined in res/xml/allowed_media_browser_callers.xml are (",
+ expectedPackages, "). This caller's certificate is: \n", signature);
+
return false;
}
- private static byte[] extractKey(String keyString) {
- try {
- return keyString.getBytes("ISO-8859-1");
- } catch (UnsupportedEncodingException e) {
- throw new AssertionError(e);
+ private final static class CallerInfo {
+ final String name;
+ final String packageName;
+ final boolean release;
+ final String signingCertificate;
+
+ public CallerInfo(String name, String packageName, boolean release,
+ String signingCertificate) {
+ this.name = name;
+ this.packageName = packageName;
+ this.release = release;
+ this.signingCertificate = signingCertificate;
}
}
}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/Playback.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/Playback.java
new file mode 100644
index 0000000..fb3ff28
--- /dev/null
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/Playback.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2014 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.mediabrowserservice;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.MediaPlayer;
+import android.media.session.PlaybackState;
+import android.net.wifi.WifiManager;
+import android.os.PowerManager;
+import android.text.TextUtils;
+
+import com.example.android.mediabrowserservice.model.MusicProvider;
+import com.example.android.mediabrowserservice.utils.LogHelper;
+import com.example.android.mediabrowserservice.utils.MediaIDHelper;
+
+import java.io.IOException;
+
+import static android.media.MediaPlayer.OnCompletionListener;
+import static android.media.MediaPlayer.OnErrorListener;
+import static android.media.MediaPlayer.OnPreparedListener;
+import static android.media.MediaPlayer.OnSeekCompleteListener;
+import static android.media.session.MediaSession.QueueItem;
+
+/**
+ * A class that implements local media playback using {@link android.media.MediaPlayer}
+ */
+public class Playback implements AudioManager.OnAudioFocusChangeListener,
+ OnCompletionListener, OnErrorListener, OnPreparedListener, OnSeekCompleteListener {
+
+ private static final String TAG = LogHelper.makeLogTag(Playback.class);
+
+ // The volume we set the media player to when we lose audio focus, but are
+ // allowed to reduce the volume instead of stopping playback.
+ public static final float VOLUME_DUCK = 0.2f;
+ // The volume we set the media player when we have audio focus.
+ public static final float VOLUME_NORMAL = 1.0f;
+
+ // we don't have audio focus, and can't duck (play at a low volume)
+ private static final int AUDIO_NO_FOCUS_NO_DUCK = 0;
+ // we don't have focus, but can duck (play at a low volume)
+ private static final int AUDIO_NO_FOCUS_CAN_DUCK = 1;
+ // we have full audio focus
+ private static final int AUDIO_FOCUSED = 2;
+
+ private final MusicService mService;
+ private final WifiManager.WifiLock mWifiLock;
+ private int mState;
+ private boolean mPlayOnFocusGain;
+ private Callback mCallback;
+ private MusicProvider mMusicProvider;
+ private volatile boolean mAudioNoisyReceiverRegistered;
+ private volatile int mCurrentPosition;
+ private volatile String mCurrentMediaId;
+
+ // Type of audio focus we have:
+ private int mAudioFocus = AUDIO_NO_FOCUS_NO_DUCK;
+ private AudioManager mAudioManager;
+ private MediaPlayer mMediaPlayer;
+
+ private IntentFilter mAudioNoisyIntentFilter =
+ new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+
+ private BroadcastReceiver mAudioNoisyReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
+ LogHelper.d(TAG, "Headphones disconnected.");
+ if (isPlaying()) {
+ Intent i = new Intent(context, MusicService.class);
+ i.setAction(MusicService.ACTION_CMD);
+ i.putExtra(MusicService.CMD_NAME, MusicService.CMD_PAUSE);
+ mService.startService(i);
+ }
+ }
+ }
+ };
+
+ public Playback(MusicService service, MusicProvider musicProvider) {
+ this.mService = service;
+ this.mMusicProvider = musicProvider;
+ this.mAudioManager = (AudioManager) service.getSystemService(Context.AUDIO_SERVICE);
+ // Create the Wifi lock (this does not acquire the lock, this just creates it)
+ this.mWifiLock = ((WifiManager) service.getSystemService(Context.WIFI_SERVICE))
+ .createWifiLock(WifiManager.WIFI_MODE_FULL, "sample_lock");
+ }
+
+ public void start() {
+ }
+
+ public void stop(boolean notifyListeners) {
+ mState = PlaybackState.STATE_STOPPED;
+ if (notifyListeners && mCallback != null) {
+ mCallback.onPlaybackStatusChanged(mState);
+ }
+ mCurrentPosition = getCurrentStreamPosition();
+ // Give up Audio focus
+ giveUpAudioFocus();
+ unregisterAudioNoisyReceiver();
+ // Relax all resources
+ relaxResources(true);
+ if (mWifiLock.isHeld()) {
+ mWifiLock.release();
+ }
+ }
+
+ public void setState(int state) {
+ this.mState = state;
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ public boolean isConnected() {
+ return true;
+ }
+
+ public boolean isPlaying() {
+ return mPlayOnFocusGain || (mMediaPlayer != null && mMediaPlayer.isPlaying());
+ }
+
+ public int getCurrentStreamPosition() {
+ return mMediaPlayer != null ?
+ mMediaPlayer.getCurrentPosition() : mCurrentPosition;
+ }
+
+ public void play(QueueItem item) {
+ mPlayOnFocusGain = true;
+ tryToGetAudioFocus();
+ registerAudioNoisyReceiver();
+ String mediaId = item.getDescription().getMediaId();
+ boolean mediaHasChanged = !TextUtils.equals(mediaId, mCurrentMediaId);
+ if (mediaHasChanged) {
+ mCurrentPosition = 0;
+ mCurrentMediaId = mediaId;
+ }
+
+ if (mState == PlaybackState.STATE_PAUSED && !mediaHasChanged && mMediaPlayer != null) {
+ configMediaPlayerState();
+ } else {
+ mState = PlaybackState.STATE_STOPPED;
+ relaxResources(false); // release everything except MediaPlayer
+ MediaMetadata track = mMusicProvider.getMusic(
+ MediaIDHelper.extractMusicIDFromMediaID(item.getDescription().getMediaId()));
+
+ String source = track.getString(MusicProvider.CUSTOM_METADATA_TRACK_SOURCE);
+
+ try {
+ createMediaPlayerIfNeeded();
+
+ mState = PlaybackState.STATE_BUFFERING;
+
+ mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mMediaPlayer.setDataSource(source);
+
+ // Starts preparing the media player in the background. When
+ // it's done, it will call our OnPreparedListener (that is,
+ // the onPrepared() method on this class, since we set the
+ // listener to 'this'). Until the media player is prepared,
+ // we *cannot* call start() on it!
+ mMediaPlayer.prepareAsync();
+
+ // If we are streaming from the internet, we want to hold a
+ // Wifi lock, which prevents the Wifi radio from going to
+ // sleep while the song is playing.
+ mWifiLock.acquire();
+
+ if (mCallback != null) {
+ mCallback.onPlaybackStatusChanged(mState);
+ }
+
+ } catch (IOException ex) {
+ LogHelper.e(TAG, ex, "Exception playing song");
+ if (mCallback != null) {
+ mCallback.onError(ex.getMessage());
+ }
+ }
+ }
+ }
+
+ public void pause() {
+ if (mState == PlaybackState.STATE_PLAYING) {
+ // Pause media player and cancel the 'foreground service' state.
+ if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+ mMediaPlayer.pause();
+ mCurrentPosition = mMediaPlayer.getCurrentPosition();
+ }
+ // while paused, retain the MediaPlayer but give up audio focus
+ relaxResources(false);
+ giveUpAudioFocus();
+ }
+ mState = PlaybackState.STATE_PAUSED;
+ if (mCallback != null) {
+ mCallback.onPlaybackStatusChanged(mState);
+ }
+ unregisterAudioNoisyReceiver();
+ }
+
+ public void seekTo(int position) {
+ LogHelper.d(TAG, "seekTo called with ", position);
+
+ if (mMediaPlayer == null) {
+ // If we do not have a current media player, simply update the current position
+ mCurrentPosition = position;
+ } else {
+ if (mMediaPlayer.isPlaying()) {
+ mState = PlaybackState.STATE_BUFFERING;
+ }
+ mMediaPlayer.seekTo(position);
+ if (mCallback != null) {
+ mCallback.onPlaybackStatusChanged(mState);
+ }
+ }
+ }
+
+ public void setCallback(Callback callback) {
+ this.mCallback = callback;
+ }
+
+ /**
+ * Try to get the system audio focus.
+ */
+ private void tryToGetAudioFocus() {
+ LogHelper.d(TAG, "tryToGetAudioFocus");
+ if (mAudioFocus != AUDIO_FOCUSED) {
+ int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ mAudioFocus = AUDIO_FOCUSED;
+ }
+ }
+ }
+
+ /**
+ * Give up the audio focus.
+ */
+ private void giveUpAudioFocus() {
+ LogHelper.d(TAG, "giveUpAudioFocus");
+ if (mAudioFocus == AUDIO_FOCUSED) {
+ if (mAudioManager.abandonAudioFocus(this) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ mAudioFocus = AUDIO_NO_FOCUS_NO_DUCK;
+ }
+ }
+ }
+
+ /**
+ * Reconfigures MediaPlayer according to audio focus settings and
+ * starts/restarts it. This method starts/restarts the MediaPlayer
+ * respecting the current audio focus state. So if we have focus, it will
+ * play normally; if we don't have focus, it will either leave the
+ * MediaPlayer paused or set it to a low volume, depending on what is
+ * allowed by the current focus settings. This method assumes mPlayer !=
+ * null, so if you are calling it, you have to do so from a context where
+ * you are sure this is the case.
+ */
+ private void configMediaPlayerState() {
+ LogHelper.d(TAG, "configMediaPlayerState. mAudioFocus=", mAudioFocus);
+ if (mAudioFocus == AUDIO_NO_FOCUS_NO_DUCK) {
+ // If we don't have audio focus and can't duck, we have to pause,
+ if (mState == PlaybackState.STATE_PLAYING) {
+ pause();
+ }
+ } else { // we have audio focus:
+ if (mAudioFocus == AUDIO_NO_FOCUS_CAN_DUCK) {
+ mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK); // we'll be relatively quiet
+ } else {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL); // we can be loud again
+ } // else do something for remote client.
+ }
+ // If we were playing when we lost focus, we need to resume playing.
+ if (mPlayOnFocusGain) {
+ if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
+ LogHelper.d(TAG,"configMediaPlayerState startMediaPlayer. seeking to ",
+ mCurrentPosition);
+ if (mCurrentPosition == mMediaPlayer.getCurrentPosition()) {
+ mMediaPlayer.start();
+ mState = PlaybackState.STATE_PLAYING;
+ } else {
+ mMediaPlayer.seekTo(mCurrentPosition);
+ mState = PlaybackState.STATE_BUFFERING;
+ }
+ }
+ mPlayOnFocusGain = false;
+ }
+ }
+ if (mCallback != null) {
+ mCallback.onPlaybackStatusChanged(mState);
+ }
+ }
+
+ /**
+ * Called by AudioManager on audio focus changes.
+ * Implementation of {@link android.media.AudioManager.OnAudioFocusChangeListener}
+ */
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ LogHelper.d(TAG, "onAudioFocusChange. focusChange=", focusChange);
+ if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ // We have gained focus:
+ mAudioFocus = AUDIO_FOCUSED;
+
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS ||
+ focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
+ focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ // We have lost focus. If we can duck (low playback volume), we can keep playing.
+ // Otherwise, we need to pause the playback.
+ boolean canDuck = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
+ mAudioFocus = canDuck ? AUDIO_NO_FOCUS_CAN_DUCK : AUDIO_NO_FOCUS_NO_DUCK;
+
+ // If we are playing, we need to reset media player by calling configMediaPlayerState
+ // with mAudioFocus properly set.
+ if (mState == PlaybackState.STATE_PLAYING && !canDuck) {
+ // If we don't have audio focus and can't duck, we save the information that
+ // we were playing, so that we can resume playback once we get the focus back.
+ mPlayOnFocusGain = true;
+ }
+ } else {
+ LogHelper.e(TAG, "onAudioFocusChange: Ignoring unsupported focusChange: ", focusChange);
+ }
+ configMediaPlayerState();
+ }
+
+ /**
+ * Called when MediaPlayer has completed a seek
+ *
+ * @see android.media.MediaPlayer.OnSeekCompleteListener
+ */
+ @Override
+ public void onSeekComplete(MediaPlayer mp) {
+ LogHelper.d(TAG, "onSeekComplete from MediaPlayer:", mp.getCurrentPosition());
+ mCurrentPosition = mp.getCurrentPosition();
+ if (mState == PlaybackState.STATE_BUFFERING) {
+ mMediaPlayer.start();
+ mState = PlaybackState.STATE_PLAYING;
+ }
+ if (mCallback != null) {
+ mCallback.onPlaybackStatusChanged(mState);
+ }
+ }
+
+ /**
+ * Called when media player is done playing current song.
+ *
+ * @see android.media.MediaPlayer.OnCompletionListener
+ */
+ @Override
+ public void onCompletion(MediaPlayer player) {
+ LogHelper.d(TAG, "onCompletion from MediaPlayer");
+ // The media player finished playing the current song, so we go ahead
+ // and start the next.
+ if (mCallback != null) {
+ mCallback.onCompletion();
+ }
+ }
+
+ /**
+ * Called when media player is done preparing.
+ *
+ * @see android.media.MediaPlayer.OnPreparedListener
+ */
+ @Override
+ public void onPrepared(MediaPlayer player) {
+ LogHelper.d(TAG, "onPrepared from MediaPlayer");
+ // The media player is done preparing. That means we can start playing if we
+ // have audio focus.
+ configMediaPlayerState();
+ }
+
+ /**
+ * Called when there's an error playing media. When this happens, the media
+ * player goes to the Error state. We warn the user about the error and
+ * reset the media player.
+ *
+ * @see android.media.MediaPlayer.OnErrorListener
+ */
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ LogHelper.e(TAG, "Media player error: what=" + what + ", extra=" + extra);
+ if (mCallback != null) {
+ mCallback.onError("MediaPlayer error " + what + " (" + extra + ")");
+ }
+ return true; // true indicates we handled the error
+ }
+
+ /**
+ * Makes sure the media player exists and has been reset. This will create
+ * the media player if needed, or reset the existing media player if one
+ * already exists.
+ */
+ private void createMediaPlayerIfNeeded() {
+ LogHelper.d(TAG, "createMediaPlayerIfNeeded. needed? ", (mMediaPlayer==null));
+ if (mMediaPlayer == null) {
+ mMediaPlayer = new MediaPlayer();
+
+ // Make sure the media player will acquire a wake-lock while
+ // playing. If we don't do that, the CPU might go to sleep while the
+ // song is playing, causing playback to stop.
+ mMediaPlayer.setWakeMode(mService.getApplicationContext(),
+ PowerManager.PARTIAL_WAKE_LOCK);
+
+ // we want the media player to notify us when it's ready preparing,
+ // and when it's done playing:
+ mMediaPlayer.setOnPreparedListener(this);
+ mMediaPlayer.setOnCompletionListener(this);
+ mMediaPlayer.setOnErrorListener(this);
+ mMediaPlayer.setOnSeekCompleteListener(this);
+ } else {
+ mMediaPlayer.reset();
+ }
+ }
+
+ /**
+ * Releases resources used by the service for playback. This includes the
+ * "foreground service" status, the wake locks and possibly the MediaPlayer.
+ *
+ * @param releaseMediaPlayer Indicates whether the Media Player should also
+ * be released or not
+ */
+ private void relaxResources(boolean releaseMediaPlayer) {
+ LogHelper.d(TAG, "relaxResources. releaseMediaPlayer=", releaseMediaPlayer);
+
+ mService.stopForeground(true);
+
+ // stop and release the Media Player, if it's available
+ if (releaseMediaPlayer && mMediaPlayer != null) {
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+
+ // we can also release the Wifi lock, if we're holding it
+ if (mWifiLock.isHeld()) {
+ mWifiLock.release();
+ }
+ }
+
+ private void registerAudioNoisyReceiver() {
+ if (!mAudioNoisyReceiverRegistered) {
+ mService.registerReceiver(mAudioNoisyReceiver, mAudioNoisyIntentFilter);
+ mAudioNoisyReceiverRegistered = true;
+ }
+ }
+
+ private void unregisterAudioNoisyReceiver() {
+ if (mAudioNoisyReceiverRegistered) {
+ mService.unregisterReceiver(mAudioNoisyReceiver);
+ mAudioNoisyReceiverRegistered = false;
+ }
+ }
+
+ interface Callback {
+ /**
+ * On current music completed.
+ */
+ void onCompletion();
+ /**
+ * on Playback status changed
+ * Implementations can use this callback to update
+ * playback state on the media sessions.
+ */
+ void onPlaybackStatusChanged(int state);
+
+ /**
+ * @param error to be added to the PlaybackState
+ */
+ void onError(String error);
+
+ }
+
+}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/QueueFragment.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/QueueFragment.java
index f6076bc..98b40e7 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/QueueFragment.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/QueueFragment.java
@@ -38,7 +38,7 @@
*/
public class QueueFragment extends Fragment {
- private static final String TAG = QueueFragment.class.getSimpleName();
+ private static final String TAG = LogHelper.makeLogTag(QueueFragment.class.getSimpleName());
private ImageButton mSkipNext;
private ImageButton mSkipPrevious;
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/model/MusicProvider.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/model/MusicProvider.java
index d0e2e0b..b56bf2a 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/model/MusicProvider.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/model/MusicProvider.java
@@ -30,12 +30,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.List;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
/**
* Utility class to get a list of MusicTrack's based on a server-side JSON
@@ -43,56 +45,54 @@
*/
public class MusicProvider {
- private static final String TAG = "MusicProvider";
+ private static final String TAG = LogHelper.makeLogTag(MusicProvider.class);
- private static final String CATALOG_URL = "http://storage.googleapis.com/automotive-media/music.json";
+ private static final String CATALOG_URL =
+ "http://storage.googleapis.com/automotive-media/music.json";
public static final String CUSTOM_METADATA_TRACK_SOURCE = "__SOURCE__";
- private static String JSON_MUSIC = "music";
- private static String JSON_TITLE = "title";
- private static String JSON_ALBUM = "album";
- private static String JSON_ARTIST = "artist";
- private static String JSON_GENRE = "genre";
- private static String JSON_SOURCE = "source";
- private static String JSON_IMAGE = "image";
- private static String JSON_TRACK_NUMBER = "trackNumber";
- private static String JSON_TOTAL_TRACK_COUNT = "totalTrackCount";
- private static String JSON_DURATION = "duration";
-
- private final ReentrantLock initializationLock = new ReentrantLock();
+ private static final String JSON_MUSIC = "music";
+ private static final String JSON_TITLE = "title";
+ private static final String JSON_ALBUM = "album";
+ private static final String JSON_ARTIST = "artist";
+ private static final String JSON_GENRE = "genre";
+ private static final String JSON_SOURCE = "source";
+ private static final String JSON_IMAGE = "image";
+ private static final String JSON_TRACK_NUMBER = "trackNumber";
+ private static final String JSON_TOTAL_TRACK_COUNT = "totalTrackCount";
+ private static final String JSON_DURATION = "duration";
// Categorized caches for music track data:
- private final HashMap<String, List<MediaMetadata>> mMusicListByGenre;
- private final HashMap<String, MediaMetadata> mMusicListById;
+ private ConcurrentMap<String, List<MediaMetadata>> mMusicListByGenre;
+ private final ConcurrentMap<String, MutableMediaMetadata> mMusicListById;
- private final HashSet<String> mFavoriteTracks;
+ private final Set<String> mFavoriteTracks;
enum State {
- NON_INITIALIZED, INITIALIZING, INITIALIZED;
+ NON_INITIALIZED, INITIALIZING, INITIALIZED
}
- private State mCurrentState = State.NON_INITIALIZED;
-
+ private volatile State mCurrentState = State.NON_INITIALIZED;
public interface Callback {
void onMusicCatalogReady(boolean success);
}
public MusicProvider() {
- mMusicListByGenre = new HashMap<>();
- mMusicListById = new HashMap<>();
- mFavoriteTracks = new HashSet<>();
+ mMusicListByGenre = new ConcurrentHashMap<>();
+ mMusicListById = new ConcurrentHashMap<>();
+ mFavoriteTracks = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
}
/**
* Get an iterator over the list of genres
*
- * @return
+ * @return genres
*/
public Iterable<String> getGenres() {
if (mCurrentState != State.INITIALIZED) {
- return new ArrayList<String>(0);
+ return Collections.emptyList();
}
return mMusicListByGenre.keySet();
}
@@ -100,11 +100,10 @@
/**
* Get music tracks of the given genre
*
- * @return
*/
public Iterable<MediaMetadata> getMusicsByGenre(String genre) {
if (mCurrentState != State.INITIALIZED || !mMusicListByGenre.containsKey(genre)) {
- return new ArrayList<MediaMetadata>();
+ return Collections.emptyList();
}
return mMusicListByGenre.get(genre);
}
@@ -113,32 +112,53 @@
* Very basic implementation of a search that filter music tracks which title containing
* the given query.
*
- * @return
*/
- public Iterable<MediaMetadata> searchMusics(String titleQuery) {
- ArrayList<MediaMetadata> result = new ArrayList<>();
+ public Iterable<MediaMetadata> searchMusic(String titleQuery) {
if (mCurrentState != State.INITIALIZED) {
- return result;
+ return Collections.emptyList();
}
+ ArrayList<MediaMetadata> result = new ArrayList<>();
titleQuery = titleQuery.toLowerCase();
- for (MediaMetadata track: mMusicListById.values()) {
- if (track.getString(MediaMetadata.METADATA_KEY_TITLE).toLowerCase()
+ for (MutableMediaMetadata track : mMusicListById.values()) {
+ if (track.metadata.getString(MediaMetadata.METADATA_KEY_TITLE).toLowerCase()
.contains(titleQuery)) {
- result.add(track);
+ result.add(track.metadata);
}
}
return result;
}
- public MediaMetadata getMusic(String mediaId) {
- return mMusicListById.get(mediaId);
+ /**
+ * Return the MediaMetadata for the given musicID.
+ *
+ * @param musicId The unique, non-hierarchical music ID.
+ */
+ public MediaMetadata getMusic(String musicId) {
+ return mMusicListById.containsKey(musicId) ? mMusicListById.get(musicId).metadata : null;
}
- public void setFavorite(String mediaId, boolean favorite) {
+ public synchronized void updateMusic(String musicId, MediaMetadata metadata) {
+ MutableMediaMetadata track = mMusicListById.get(musicId);
+ if (track == null) {
+ return;
+ }
+
+ String oldGenre = track.metadata.getString(MediaMetadata.METADATA_KEY_GENRE);
+ String newGenre = metadata.getString(MediaMetadata.METADATA_KEY_GENRE);
+
+ track.metadata = metadata;
+
+ // if genre has changed, we need to rebuild the list by genre
+ if (!oldGenre.equals(newGenre)) {
+ buildListsByGenre();
+ }
+ }
+
+ public void setFavorite(String musicId, boolean favorite) {
if (favorite) {
- mFavoriteTracks.add(mediaId);
+ mFavoriteTracks.add(musicId);
} else {
- mFavoriteTracks.remove(mediaId);
+ mFavoriteTracks.remove(musicId);
}
}
@@ -152,12 +172,10 @@
/**
* Get the list of music tracks from a server and caches the track information
- * for future reference, keying tracks by mediaId and grouping by genre.
- *
- * @return
+ * for future reference, keying tracks by musicId and grouping by genre.
*/
- public void retrieveMedia(final Callback callback) {
-
+ public void retrieveMediaAsync(final Callback callback) {
+ LogHelper.d(TAG, "retrieveMediaAsync called");
if (mCurrentState == State.INITIALIZED) {
// Nothing to do, execute callback immediately
callback.onMusicCatalogReady(true);
@@ -165,44 +183,60 @@
}
// Asynchronously load the music catalog in a separate thread
- new AsyncTask() {
+ new AsyncTask<Void, Void, State>() {
@Override
- protected Object doInBackground(Object[] objects) {
- retrieveMediaAsync(callback);
- return null;
+ protected State doInBackground(Void... params) {
+ retrieveMedia();
+ return mCurrentState;
+ }
+
+ @Override
+ protected void onPostExecute(State current) {
+ if (callback != null) {
+ callback.onMusicCatalogReady(current == State.INITIALIZED);
+ }
}
}.execute();
}
- private void retrieveMediaAsync(Callback callback) {
- initializationLock.lock();
+ private synchronized void buildListsByGenre() {
+ ConcurrentMap<String, List<MediaMetadata>> newMusicListByGenre = new ConcurrentHashMap<>();
+ for (MutableMediaMetadata m : mMusicListById.values()) {
+ String genre = m.metadata.getString(MediaMetadata.METADATA_KEY_GENRE);
+ List<MediaMetadata> list = newMusicListByGenre.get(genre);
+ if (list == null) {
+ list = new ArrayList<>();
+ newMusicListByGenre.put(genre, list);
+ }
+ list.add(m.metadata);
+ }
+ mMusicListByGenre = newMusicListByGenre;
+ }
+
+ private synchronized void retrieveMedia() {
try {
if (mCurrentState == State.NON_INITIALIZED) {
mCurrentState = State.INITIALIZING;
int slashPos = CATALOG_URL.lastIndexOf('/');
String path = CATALOG_URL.substring(0, slashPos + 1);
- JSONObject jsonObj = parseUrl(CATALOG_URL);
-
+ JSONObject jsonObj = fetchJSONFromUrl(CATALOG_URL);
+ if (jsonObj == null) {
+ return;
+ }
JSONArray tracks = jsonObj.getJSONArray(JSON_MUSIC);
if (tracks != null) {
for (int j = 0; j < tracks.length(); j++) {
MediaMetadata item = buildFromJSON(tracks.getJSONObject(j), path);
- String genre = item.getString(MediaMetadata.METADATA_KEY_GENRE);
- List<MediaMetadata> list = mMusicListByGenre.get(genre);
- if (list == null) {
- list = new ArrayList<>();
- }
- list.add(item);
- mMusicListByGenre.put(genre, list);
- mMusicListById.put(item.getString(MediaMetadata.METADATA_KEY_MEDIA_ID),
- item);
+ String musicId = item.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+ mMusicListById.put(musicId, new MutableMediaMetadata(musicId, item));
}
+ buildListsByGenre();
}
mCurrentState = State.INITIALIZED;
}
- } catch (RuntimeException | JSONException e) {
+ } catch (JSONException e) {
LogHelper.e(TAG, e, "Could not retrieve music list");
} finally {
if (mCurrentState != State.INITIALIZED) {
@@ -210,10 +244,6 @@
// retries (eg if the network connection is temporary unavailable)
mCurrentState = State.NON_INITIALIZED;
}
- initializationLock.unlock();
- if (callback != null) {
- callback.onMusicCatalogReady(mCurrentState == State.INITIALIZED);
- }
}
}
@@ -263,19 +293,18 @@
* Download a JSON file from a server, parse the content and return the JSON
* object.
*
- * @param urlString
- * @return
+ * @return result JSONObject containing the parsed representation.
*/
- private JSONObject parseUrl(String urlString) {
+ private JSONObject fetchJSONFromUrl(String urlString) {
InputStream is = null;
try {
- java.net.URL url = new java.net.URL(urlString);
+ URL url = new URL(urlString);
URLConnection urlConnection = url.openConnection();
is = new BufferedInputStream(urlConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(
urlConnection.getInputStream(), "iso-8859-1"));
StringBuilder sb = new StringBuilder();
- String line = null;
+ String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
@@ -293,4 +322,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/model/MutableMediaMetadata.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/model/MutableMediaMetadata.java
new file mode 100644
index 0000000..1ee9d61
--- /dev/null
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/model/MutableMediaMetadata.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 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.mediabrowserservice.model;
+
+import android.media.MediaMetadata;
+import android.text.TextUtils;
+
+/**
+ * Holder class that encapsulates a MediaMetadata and allows the actual metadata to be modified
+ * without requiring to rebuild the collections the metadata is in.
+ */
+public class MutableMediaMetadata {
+
+ public MediaMetadata metadata;
+ public final String trackId;
+
+ public MutableMediaMetadata(String trackId, MediaMetadata metadata) {
+ this.metadata = metadata;
+ this.trackId = trackId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != MutableMediaMetadata.class) {
+ return false;
+ }
+
+ MutableMediaMetadata that = (MutableMediaMetadata) o;
+
+ return TextUtils.equals(trackId, that.trackId);
+ }
+
+ @Override
+ public int hashCode() {
+ return trackId.hashCode();
+ }
+}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/BitmapHelper.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/BitmapHelper.java
index 5f0e767..7325130 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/BitmapHelper.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/BitmapHelper.java
@@ -18,22 +18,26 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class BitmapHelper {
+ private static final String TAG = LogHelper.makeLogTag(BitmapHelper.class);
- // Bitmap size for album art in media notifications when there are more than 3 playback actions
- public static final int MEDIA_ART_SMALL_WIDTH=64;
- public static final int MEDIA_ART_SMALL_HEIGHT=64;
+ // Max read limit that we allow our input stream to mark/reset.
+ private static final int MAX_READ_LIMIT_PER_IMG = 1024 * 1024;
- // Bitmap size for album art in media notifications when there are no more than 3 playback actions
- public static final int MEDIA_ART_BIG_WIDTH=128;
- public static final int MEDIA_ART_BIG_HEIGHT=128;
+ public static Bitmap scaleBitmap(Bitmap src, int maxWidth, int maxHeight) {
+ double scaleFactor = Math.min(
+ ((double) maxWidth)/src.getWidth(), ((double) maxHeight)/src.getHeight());
+ return Bitmap.createScaledBitmap(src,
+ (int) (src.getWidth() * scaleFactor), (int) (src.getHeight() * scaleFactor), false);
+ }
- public static final Bitmap scaleBitmap(int scaleFactor, InputStream is) {
+ public static Bitmap scaleBitmap(int scaleFactor, InputStream is) {
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
@@ -41,11 +45,10 @@
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
- Bitmap bitmap = BitmapFactory.decodeStream(is, null, bmOptions);
- return bitmap;
+ return BitmapFactory.decodeStream(is, null, bmOptions);
}
- public static final int findScaleFactor(int targetW, int targetH, InputStream is) {
+ public static int findScaleFactor(int targetW, int targetH, InputStream is) {
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
@@ -57,21 +60,24 @@
return Math.min(actualW/targetW, actualH/targetH);
}
- public static final Bitmap fetchAndRescaleBitmap(String uri, int width, int height)
+ @SuppressWarnings("SameParameterValue")
+ public static Bitmap fetchAndRescaleBitmap(String uri, int width, int height)
throws IOException {
URL url = new URL(uri);
- HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
- httpConnection.setDoInput(true);
- httpConnection.connect();
- InputStream inputStream = httpConnection.getInputStream();
- int scaleFactor = findScaleFactor(width, height, inputStream);
-
- httpConnection = (HttpURLConnection) url.openConnection();
- httpConnection.setDoInput(true);
- httpConnection.connect();
- inputStream = httpConnection.getInputStream();
- Bitmap bitmap = scaleBitmap(scaleFactor, inputStream);
- return bitmap;
+ BufferedInputStream is = null;
+ try {
+ HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ is = new BufferedInputStream(urlConnection.getInputStream());
+ is.mark(MAX_READ_LIMIT_PER_IMG);
+ int scaleFactor = findScaleFactor(width, height, is);
+ LogHelper.d(TAG, "Scaling bitmap ", uri, " by factor ", scaleFactor, " to support ",
+ width, "x", height, "requested dimension");
+ is.reset();
+ return scaleBitmap(scaleFactor, is);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
}
-
}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/CarHelper.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/CarHelper.java
new file mode 100644
index 0000000..74861ba
--- /dev/null
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/CarHelper.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 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.mediabrowserservice.utils;
+
+import android.os.Bundle;
+
+public class CarHelper {
+ private static final String AUTO_APP_PACKAGE_NAME = "com.google.android.projection.gearhead";
+
+ // Use these extras to reserve space for the corresponding actions, even when they are disabled
+ // in the playbackstate, so the custom actions don't reflow.
+ private static final String SLOT_RESERVATION_SKIP_TO_NEXT =
+ "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT";
+ private static final String SLOT_RESERVATION_SKIP_TO_PREV =
+ "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS";
+ private static final String SLOT_RESERVATION_QUEUE =
+ "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE";
+
+
+ public static boolean isValidCarPackage(String packageName) {
+ return AUTO_APP_PACKAGE_NAME.equals(packageName);
+ }
+
+ public static void setSlotReservationFlags(Bundle extras, boolean reservePlayingQueueSlot,
+ boolean reserveSkipToNextSlot, boolean reserveSkipToPrevSlot) {
+ if (reservePlayingQueueSlot) {
+ extras.putBoolean(SLOT_RESERVATION_QUEUE, true);
+ } else {
+ extras.remove(SLOT_RESERVATION_QUEUE);
+ }
+ if (reserveSkipToPrevSlot) {
+ extras.putBoolean(SLOT_RESERVATION_SKIP_TO_PREV, true);
+ } else {
+ extras.remove(SLOT_RESERVATION_SKIP_TO_PREV);
+ }
+ if (reserveSkipToNextSlot) {
+ extras.putBoolean(SLOT_RESERVATION_SKIP_TO_NEXT, true);
+ } else {
+ extras.remove(SLOT_RESERVATION_SKIP_TO_NEXT);
+ }
+ }
+}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/LogHelper.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/LogHelper.java
index 92b2e09..09d14d2 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/LogHelper.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/LogHelper.java
@@ -17,13 +17,42 @@
import android.util.Log;
+import com.example.android.mediabrowserservice.BuildConfig;
+
public class LogHelper {
+
+ private static final String LOG_PREFIX = "sample_";
+ private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length();
+ private static final int MAX_LOG_TAG_LENGTH = 23;
+
+ public static String makeLogTag(String str) {
+ if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) {
+ return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1);
+ }
+
+ return LOG_PREFIX + str;
+ }
+
+ /**
+ * Don't use this when obfuscating class names!
+ */
+ public static String makeLogTag(Class cls) {
+ return makeLogTag(cls.getSimpleName());
+ }
+
+
public static void v(String tag, Object... messages) {
- log(tag, Log.VERBOSE, null, messages);
+ // Only log VERBOSE if build type is DEBUG
+ if (BuildConfig.DEBUG) {
+ log(tag, Log.VERBOSE, null, messages);
+ }
}
public static void d(String tag, Object... messages) {
- log(tag, Log.DEBUG, null, messages);
+ // Only log DEBUG if build type is DEBUG
+ if (BuildConfig.DEBUG) {
+ log(tag, Log.DEBUG, null, messages);
+ }
}
public static void i(String tag, Object... messages) {
@@ -47,13 +76,14 @@
}
public static void log(String tag, int level, Throwable t, Object... messages) {
- if (messages != null && Log.isLoggable(tag, level)) {
+ if (Log.isLoggable(tag, level)) {
String message;
- if (messages.length == 1) {
- message = messages[0] == null ? null : messages[0].toString();
+ if (t == null && messages != null && messages.length == 1) {
+ // handle this common case without the extra cost of creating a stringbuffer:
+ message = messages[0].toString();
} else {
StringBuilder sb = new StringBuilder();
- for (Object m: messages) {
+ if (messages != null) for (Object m : messages) {
sb.append(m);
}
if (t != null) {
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/MediaIDHelper.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/MediaIDHelper.java
index f66a010..604cf8a 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/MediaIDHelper.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/MediaIDHelper.java
@@ -16,32 +16,44 @@
package com.example.android.mediabrowserservice.utils;
-import android.media.MediaMetadata;
+import java.util.Arrays;
/**
* Utility class to help on queue related tasks.
*/
public class MediaIDHelper {
- private static final String TAG = "MediaIDHelper";
+ private static final String TAG = LogHelper.makeLogTag(MediaIDHelper.class);
// Media IDs used on browseable items of MediaBrowser
public static final String MEDIA_ID_ROOT = "__ROOT__";
public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
+ public static final String MEDIA_ID_MUSICS_BY_SEARCH = "__BY_SEARCH__";
- public static final String createTrackMediaID(String categoryType, String categoryValue,
- MediaMetadata track) {
- // MediaIDs are of the form <categoryType>/<categoryValue>|<musicUniqueId>, to make it easy to
- // find the category (like genre) that a music was selected from, so we
+ private static final char CATEGORY_SEPARATOR = '/';
+ private static final char LEAF_SEPARATOR = '|';
+
+ public static String createMediaID(String musicID, String... categories) {
+ // MediaIDs are of the form <categoryType>/<categoryValue>|<musicUniqueId>, to make it easy
+ // to find the category (like genre) that a music was selected from, so we
// can correctly build the playing queue. This is specially useful when
// one music can appear in more than one list, like "by genre -> genre_1"
// and "by artist -> artist_1".
- return categoryType + "/" + categoryValue + "|" +
- track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID);
+ StringBuilder sb = new StringBuilder();
+ if (categories != null && categories.length > 0) {
+ sb.append(categories[0]);
+ for (int i=1; i < categories.length; i++) {
+ sb.append(CATEGORY_SEPARATOR).append(categories[i]);
+ }
+ }
+ if (musicID != null) {
+ sb.append(LEAF_SEPARATOR).append(musicID);
+ }
+ return sb.toString();
}
- public static final String createBrowseCategoryMediaID(String categoryType, String categoryValue) {
- return categoryType + "/" + categoryValue;
+ public static String createBrowseCategoryMediaID(String categoryType, String categoryValue) {
+ return categoryType + CATEGORY_SEPARATOR + categoryValue;
}
/**
@@ -50,12 +62,15 @@
* musicID. This is necessary so we know where the user selected the music from, when the music
* exists in more than one music list, and thus we are able to correctly build the playing queue.
*
- * @param musicID
- * @return
+ * @param mediaID that contains the musicID
+ * @return musicID
*/
- public static final String extractMusicIDFromMediaID(String musicID) {
- String[] segments = musicID.split("\\|", 2);
- return segments.length == 2 ? segments[1] : null;
+ public static String extractMusicIDFromMediaID(String mediaID) {
+ int pos = mediaID.indexOf(LEAF_SEPARATOR);
+ if (pos >= 0) {
+ return mediaID.substring(pos+1);
+ }
+ return null;
}
/**
@@ -64,25 +79,37 @@
* mediaID. This is necessary so we know where the user selected the music from, when the music
* exists in more than one music list, and thus we are able to correctly build the playing queue.
*
- * @param mediaID
- * @return
+ * @param mediaID that contains a category and categoryValue.
*/
- public static final String[] extractBrowseCategoryFromMediaID(String mediaID) {
- if (mediaID.indexOf('|') >= 0) {
- mediaID = mediaID.split("\\|")[0];
+ public static String[] getHierarchy(String mediaID) {
+ int pos = mediaID.indexOf(LEAF_SEPARATOR);
+ if (pos >= 0) {
+ mediaID = mediaID.substring(0, pos);
}
- if (mediaID.indexOf('/') == 0) {
- return new String[]{mediaID, null};
- } else {
- return mediaID.split("/", 2);
- }
+ return mediaID.split(String.valueOf(CATEGORY_SEPARATOR));
}
- public static final String extractBrowseCategoryValueFromMediaID(String mediaID) {
- String[] categoryAndValue = extractBrowseCategoryFromMediaID(mediaID);
- if (categoryAndValue != null && categoryAndValue.length == 2) {
- return categoryAndValue[1];
+ public static String extractBrowseCategoryValueFromMediaID(String mediaID) {
+ String[] hierarchy = getHierarchy(mediaID);
+ if (hierarchy != null && hierarchy.length == 2) {
+ return hierarchy[1];
}
return null;
}
-}
\ No newline at end of file
+
+ private static boolean isBrowseable(String mediaID) {
+ return mediaID.indexOf(LEAF_SEPARATOR) < 0;
+ }
+
+ public static String getParentMediaID(String mediaID) {
+ String[] hierarchy = getHierarchy(mediaID);
+ if (!isBrowseable(mediaID)) {
+ return createMediaID(null, hierarchy);
+ }
+ if (hierarchy == null || hierarchy.length <= 1) {
+ return MEDIA_ID_ROOT;
+ }
+ String[] parentHierarchy = Arrays.copyOf(hierarchy, hierarchy.length-1);
+ return createMediaID(null, parentHierarchy);
+ }
+}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/QueueHelper.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/QueueHelper.java
index 980efaa..9a2caa8 100644
--- a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/QueueHelper.java
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/QueueHelper.java
@@ -22,52 +22,64 @@
import com.example.android.mediabrowserservice.model.MusicProvider;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_GENRE;
+import static com.example.android.mediabrowserservice.utils.MediaIDHelper.MEDIA_ID_MUSICS_BY_SEARCH;
/**
* Utility class to help on queue related tasks.
*/
public class QueueHelper {
- private static final String TAG = "QueueHelper";
+ private static final String TAG = LogHelper.makeLogTag(QueueHelper.class);
- public static final List<MediaSession.QueueItem> getPlayingQueue(String mediaId,
+ public static List<MediaSession.QueueItem> getPlayingQueue(String mediaId,
MusicProvider musicProvider) {
- // extract the category and unique music ID from the media ID:
- String[] category = MediaIDHelper.extractBrowseCategoryFromMediaID(mediaId);
+ // extract the browsing hierarchy from the media ID:
+ String[] hierarchy = MediaIDHelper.getHierarchy(mediaId);
- // This sample only supports genre category.
- if (!category[0].equals(MEDIA_ID_MUSICS_BY_GENRE) || category.length != 2) {
+ if (hierarchy.length != 2) {
LogHelper.e(TAG, "Could not build a playing queue for this mediaId: ", mediaId);
return null;
}
- String categoryValue = category[1];
- LogHelper.e(TAG, "Creating playing queue for musics of genre ", categoryValue);
+ String categoryType = hierarchy[0];
+ String categoryValue = hierarchy[1];
+ LogHelper.d(TAG, "Creating playing queue for ", categoryType, ", ", categoryValue);
- List<MediaSession.QueueItem> queue = convertToQueue(
- musicProvider.getMusicsByGenre(categoryValue));
+ Iterable<MediaMetadata> tracks = null;
+ // This sample only supports genre and by_search category types.
+ if (categoryType.equals(MEDIA_ID_MUSICS_BY_GENRE)) {
+ tracks = musicProvider.getMusicsByGenre(categoryValue);
+ } else if (categoryType.equals(MEDIA_ID_MUSICS_BY_SEARCH)) {
+ tracks = musicProvider.searchMusic(categoryValue);
+ }
- return queue;
+ if (tracks == null) {
+ LogHelper.e(TAG, "Unrecognized category type: ", categoryType, " for mediaId ", mediaId);
+ return null;
+ }
+
+ return convertToQueue(tracks, hierarchy[0], hierarchy[1]);
}
- public static final List<MediaSession.QueueItem> getPlayingQueueFromSearch(String query,
+ public static List<MediaSession.QueueItem> getPlayingQueueFromSearch(String query,
MusicProvider musicProvider) {
- LogHelper.e(TAG, "Creating playing queue for musics from search ", query);
+ LogHelper.d(TAG, "Creating playing queue for musics from search ", query);
- return convertToQueue(musicProvider.searchMusics(query));
+ return convertToQueue(musicProvider.searchMusic(query), MEDIA_ID_MUSICS_BY_SEARCH, query);
}
- public static final int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
+ public static int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
String mediaId) {
int index = 0;
- for (MediaSession.QueueItem item: queue) {
+ for (MediaSession.QueueItem item : queue) {
if (mediaId.equals(item.getDescription().getMediaId())) {
return index;
}
@@ -76,10 +88,10 @@
return -1;
}
- public static final int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
+ public static int getMusicIndexOnQueue(Iterable<MediaSession.QueueItem> queue,
long queueId) {
int index = 0;
- for (MediaSession.QueueItem item: queue) {
+ for (MediaSession.QueueItem item : queue) {
if (queueId == item.getQueueId()) {
return index;
}
@@ -88,15 +100,25 @@
return -1;
}
- private static final List<MediaSession.QueueItem> convertToQueue(
- Iterable<MediaMetadata> tracks) {
+ private static List<MediaSession.QueueItem> convertToQueue(
+ Iterable<MediaMetadata> tracks, String... categories) {
List<MediaSession.QueueItem> queue = new ArrayList<>();
int count = 0;
for (MediaMetadata track : tracks) {
+
+ // We create a hierarchy-aware mediaID, so we know what the queue is about by looking
+ // at the QueueItem media IDs.
+ String hierarchyAwareMediaID = MediaIDHelper.createMediaID(
+ track.getDescription().getMediaId(), categories);
+
+ MediaMetadata trackCopy = new MediaMetadata.Builder(track)
+ .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, hierarchyAwareMediaID)
+ .build();
+
// We don't expect queues to change after created, so we use the item index as the
// queueId. Any other number unique in the queue would work.
MediaSession.QueueItem item = new MediaSession.QueueItem(
- track.getDescription(), count++);
+ trackCopy.getDescription(), count++);
queue.add(item);
}
return queue;
@@ -105,25 +127,23 @@
/**
* Create a random queue. For simplicity sake, instead of a random queue, we create a
- * queue using the first genre,
+ * queue using the first genre.
*
- * @param musicProvider
- * @return
+ * @param musicProvider the provider used for fetching music.
+ * @return list containing {@link android.media.session.MediaSession.QueueItem}'s
*/
- public static final List<MediaSession.QueueItem> getRandomQueue(MusicProvider musicProvider) {
+ public static List<MediaSession.QueueItem> getRandomQueue(MusicProvider musicProvider) {
Iterator<String> genres = musicProvider.getGenres().iterator();
if (!genres.hasNext()) {
- return new ArrayList<>();
+ return Collections.emptyList();
}
String genre = genres.next();
Iterable<MediaMetadata> tracks = musicProvider.getMusicsByGenre(genre);
- return convertToQueue(tracks);
+ return convertToQueue(tracks, MEDIA_ID_MUSICS_BY_GENRE, genre);
}
-
-
- public static final boolean isIndexPlayable(int index, List<MediaSession.QueueItem> queue) {
+ public static boolean isIndexPlayable(int index, List<MediaSession.QueueItem> queue) {
return (queue != null && index >= 0 && index < queue.size());
}
}
diff --git a/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/ResourceHelper.java b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/ResourceHelper.java
new file mode 100644
index 0000000..ed4dcd0
--- /dev/null
+++ b/samples/browseable/MediaBrowserService/src/com.example.android.mediabrowserservice/utils/ResourceHelper.java
@@ -0,0 +1,53 @@
+/*
+* Copyright (C) 2014 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.mediabrowserservice.utils;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+
+/**
+ * Generic reusable methods to handle resources.
+ */
+public class ResourceHelper {
+ /**
+ * Get a color value from a theme attribute.
+ * @param context used for getting the color.
+ * @param attribute theme attribute.
+ * @param defaultColor default to use.
+ * @return color value
+ */
+ public static int getThemeColor(Context context, int attribute, int defaultColor) {
+ int themeColor = 0;
+ String packageName = context.getPackageName();
+ try {
+ Context packageContext = context.createPackageContext(packageName, 0);
+ ApplicationInfo applicationInfo =
+ context.getPackageManager().getApplicationInfo(packageName, 0);
+ packageContext.setTheme(applicationInfo.theme);
+ Resources.Theme theme = packageContext.getTheme();
+ TypedArray ta = theme.obtainStyledAttributes(new int[] {attribute});
+ themeColor = ta.getColor(0, defaultColor);
+ ta.recycle();
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ return themeColor;
+ }
+}
diff --git a/samples/browseable/MediaRecorder/res/layout/activity_main.xml b/samples/browseable/MediaRecorder/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/MediaRecorder/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/MediaRouter/res/layout/activity_main.xml b/samples/browseable/MediaRouter/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/MediaRouter/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/NetworkConnect/res/layout/activity_main.xml b/samples/browseable/NetworkConnect/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/NetworkConnect/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/NfcProvisioning/AndroidManifest.xml b/samples/browseable/NfcProvisioning/AndroidManifest.xml
new file mode 100644
index 0000000..1db70d7
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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
+ package="com.example.android.nfcprovisioning"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.NFC"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme">
+
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:windowSoftInputMode="stateHidden">
+ <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/NfcProvisioning/_index.jd b/samples/browseable/NfcProvisioning/_index.jd
new file mode 100644
index 0000000..1276604
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/_index.jd
@@ -0,0 +1,11 @@
+page.tags="NfcProvisioning"
+sample.group=Device Admin
+@jd:body
+
+<p>
+
+This sample demonstrates how to provision other devices with a specified device owner using NFC.
+Install the DeviceOwner sample to an unprovisioned device. Fill in the values below, and bump the
+two devices, and tap this side to initiate the provisioning process.
+
+ </p>
diff --git a/samples/browseable/NfcProvisioning/res/drawable-hdpi/ic_launcher.png b/samples/browseable/NfcProvisioning/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..638a88d
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/NfcProvisioning/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
copy to samples/browseable/NfcProvisioning/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/NfcProvisioning/res/drawable-mdpi/ic_launcher.png b/samples/browseable/NfcProvisioning/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..cd128c9
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/NfcProvisioning/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/NfcProvisioning/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..824541a
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/NfcProvisioning/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/NfcProvisioning/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..5fe9f2e
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/NfcProvisioning/res/layout-w720dp/activity_main.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml
rename to samples/browseable/NfcProvisioning/res/layout-w720dp/activity_main.xml
diff --git a/samples/browseable/AdapterTransition/res/layout/activity_main.xml b/samples/browseable/NfcProvisioning/res/layout/activity_main.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/layout/activity_main.xml
rename to samples/browseable/NfcProvisioning/res/layout/activity_main.xml
diff --git a/samples/browseable/NfcProvisioning/res/layout/fragment_nfc_provisioning.xml b/samples/browseable/NfcProvisioning/res/layout/fragment_nfc_provisioning.xml
new file mode 100644
index 0000000..ee79e7a
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/res/layout/fragment_nfc_provisioning.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 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.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="@dimen/margin_small">
+
+ <EditText
+ android:id="@+id/package_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_small"
+ android:hint="@string/hint_package_name"
+ android:inputType="textNoSuggestions"/>
+
+ <EditText
+ android:id="@+id/locale"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_small"
+ android:hint="@string/hint_locale"
+ android:inputType="textNoSuggestions"/>
+
+ <EditText
+ android:id="@+id/timezone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_small"
+ android:hint="@string/hint_timezone"
+ android:inputType="textNoSuggestions"/>
+
+ <EditText
+ android:id="@+id/wifi_ssid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_small"
+ android:hint="@string/hint_wifi_ssid"
+ android:inputType="textNoSuggestions"/>
+
+ <EditText
+ android:id="@+id/wifi_security_type"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_small"
+ android:hint="@string/hint_wifi_security_type"
+ android:inputType="textNoSuggestions"/>
+
+ <EditText
+ android:id="@+id/wifi_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/margin_small"
+ android:hint="@string/hint_wifi_password"
+ android:inputType="textPassword"/>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/samples/browseable/AdapterTransition/res/menu/main.xml b/samples/browseable/NfcProvisioning/res/menu/main.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/menu/main.xml
rename to samples/browseable/NfcProvisioning/res/menu/main.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/NfcProvisioning/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/NfcProvisioning/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/NfcProvisioning/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
copy to samples/browseable/NfcProvisioning/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/NfcProvisioning/res/values-v11/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
copy to samples/browseable/NfcProvisioning/res/values-v11/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-colors.xml b/samples/browseable/NfcProvisioning/res/values-v21/base-colors.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-colors.xml
copy to samples/browseable/NfcProvisioning/res/values-v21/base-colors.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml b/samples/browseable/NfcProvisioning/res/values-v21/base-template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml
copy to samples/browseable/NfcProvisioning/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml b/samples/browseable/NfcProvisioning/res/values/base-strings.xml
similarity index 70%
copy from samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
copy to samples/browseable/NfcProvisioning/res/values/base-strings.xml
index d653382..0114f3e 100644
--- a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
+++ b/samples/browseable/NfcProvisioning/res/values/base-strings.xml
@@ -15,12 +15,14 @@
limitations under the License.
-->
<resources>
- <string name="app_name">WatchViewStub</string>
+ <string name="app_name">NfcProvisioning</string>
<string name="intro_message">
<![CDATA[
- This sample demonstrates how to specify different layouts for round and rectangular screens.
+This sample demonstrates how to provision other devices with a specified device owner using NFC.
+Install the DeviceOwner sample to an unprovisioned device. Fill in the values below, and bump the
+two devices, and tap this side to initiate the provisioning process.
]]>
diff --git a/samples/browseable/AdapterTransition/res/values/fragmentview_strings.xml b/samples/browseable/NfcProvisioning/res/values/fragmentview_strings.xml
similarity index 100%
rename from samples/browseable/AdapterTransition/res/values/fragmentview_strings.xml
rename to samples/browseable/NfcProvisioning/res/values/fragmentview_strings.xml
diff --git a/samples/browseable/NfcProvisioning/res/values/strings.xml b/samples/browseable/NfcProvisioning/res/values/strings.xml
new file mode 100644
index 0000000..67de389
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 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>
+
+ <string name="nfc_comment">NFC provisioning sample</string>
+
+ <string name="hint_package_name">Package name of device owner</string>
+ <string name="hint_locale">Locale</string>
+ <string name="hint_timezone">Timezone</string>
+ <string name="hint_wifi_ssid">WiFi SSID</string>
+ <string name="hint_wifi_security_type">WiFi security type</string>
+ <string name="hint_wifi_password">WiFi password</string>
+
+</resources>
diff --git a/samples/browseable/AdapterTransition/res/values/template-dimens.xml b/samples/browseable/NfcProvisioning/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-dimens.xml
copy to samples/browseable/NfcProvisioning/res/values/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values/template-styles.xml b/samples/browseable/NfcProvisioning/res/values/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-styles.xml
copy to samples/browseable/NfcProvisioning/res/values/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/NfcProvisioning/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/NfcProvisioning/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/NfcProvisioning/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
copy to samples/browseable/NfcProvisioning/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/NfcProvisioning/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/NfcProvisioning/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/NfcProvisioning/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/MainActivity.java
similarity index 96%
rename from samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
rename to samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/MainActivity.java
index 0adb842..768e1c8 100644
--- a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
+++ b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/MainActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.adaptertransition;
+package com.example.android.nfcprovisioning;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
@@ -49,7 +49,7 @@
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- AdapterTransitionFragment fragment = new AdapterTransitionFragment();
+ NfcProvisioningFragment fragment = new NfcProvisioningFragment();
transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();
}
diff --git a/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/NfcProvisioningFragment.java b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/NfcProvisioningFragment.java
new file mode 100644
index 0000000..f46d0f2
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/NfcProvisioningFragment.java
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+package com.example.android.nfcprovisioning;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcEvent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Provides UI and logic for NFC provisioning.
+ * <p>
+ * This fragment creates an intent, which sends parameters to a second device via an Nfc bump. If
+ * the second device is factory reset, this will start provisioning the second device to set it up
+ * as an owned device.
+ * </p>
+ */
+public class NfcProvisioningFragment extends Fragment implements
+ NfcAdapter.CreateNdefMessageCallback,
+ TextWatcherWrapper.OnTextChangedListener,
+ LoaderManager.LoaderCallbacks<Map<String, String>> {
+
+ private static final int LOADER_PROVISIONING_VALUES = 1;
+
+ // View references
+ private EditText mEditPackageName;
+ private EditText mEditLocale;
+ private EditText mEditTimezone;
+ private EditText mEditWifiSsid;
+ private EditText mEditWifiSecurityType;
+ private EditText mEditWifiPassword;
+
+ // Values to be set via NFC bump
+ private Map<String, String> mProvisioningValues;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_nfc_provisioning, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ // Retrieve view references
+ mEditPackageName = (EditText) view.findViewById(R.id.package_name);
+ mEditLocale = (EditText) view.findViewById(R.id.locale);
+ mEditTimezone = (EditText) view.findViewById(R.id.timezone);
+ mEditWifiSsid = (EditText) view.findViewById(R.id.wifi_ssid);
+ mEditWifiSecurityType = (EditText) view.findViewById(R.id.wifi_security_type);
+ mEditWifiPassword = (EditText) view.findViewById(R.id.wifi_password);
+ // Bind event handlers
+ mEditPackageName.addTextChangedListener(new TextWatcherWrapper(R.id.package_name, this));
+ mEditLocale.addTextChangedListener(new TextWatcherWrapper(R.id.locale, this));
+ mEditTimezone.addTextChangedListener(new TextWatcherWrapper(R.id.timezone, this));
+ mEditWifiSsid.addTextChangedListener(new TextWatcherWrapper(R.id.wifi_ssid, this));
+ mEditWifiSecurityType.addTextChangedListener(
+ new TextWatcherWrapper(R.id.wifi_security_type, this));
+ mEditWifiPassword.addTextChangedListener(new TextWatcherWrapper(R.id.wifi_password, this));
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Activity activity = getActivity();
+ NfcAdapter adapter = NfcAdapter.getDefaultAdapter(activity);
+ if (adapter != null) {
+ adapter.setNdefPushMessageCallback(this, activity);
+ }
+ getLoaderManager().initLoader(LOADER_PROVISIONING_VALUES, null, this);
+ }
+
+ @Override
+ public NdefMessage createNdefMessage(NfcEvent event) {
+ if (mProvisioningValues == null) {
+ return null;
+ }
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ Properties properties = new Properties();
+ // Store all the values into the Properties object
+ for (Map.Entry<String, String> e : mProvisioningValues.entrySet()) {
+ if (!TextUtils.isEmpty(e.getValue())) {
+ String value;
+ if (e.getKey().equals(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID)) {
+ // Make sure to surround SSID with double quotes
+ value = e.getValue();
+ if (!value.startsWith("\"") || !value.endsWith("\"")) {
+ value = "\"" + value + "\"";
+ }
+ } else {
+ value = e.getValue();
+ }
+ properties.put(e.getKey(), value);
+ }
+ }
+ try {
+ properties.store(stream, getString(R.string.nfc_comment));
+ NdefRecord record = NdefRecord.createMime(
+ DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC, stream.toByteArray());
+ return new NdefMessage(new NdefRecord[]{record});
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public void onTextChanged(int id, String s) {
+ if (mProvisioningValues == null) {
+ return;
+ }
+ switch (id) {
+ case R.id.package_name:
+ mProvisioningValues.put(
+ DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, s);
+ break;
+ case R.id.locale:
+ mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_LOCALE, s);
+ break;
+ case R.id.timezone:
+ mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE, s);
+ break;
+ case R.id.wifi_ssid:
+ mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID, s);
+ break;
+ case R.id.wifi_security_type:
+ mProvisioningValues.put(
+ DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SECURITY_TYPE, s);
+ break;
+ case R.id.wifi_password:
+ mProvisioningValues.put(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PASSWORD, s);
+ break;
+ }
+ }
+
+ @Override
+ public Loader<Map<String, String>> onCreateLoader(int id, Bundle args) {
+ if (id == LOADER_PROVISIONING_VALUES) {
+ return new ProvisioningValuesLoader(getActivity());
+ }
+ return null;
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Map<String, String>> loader, Map<String, String> values) {
+ if (loader.getId() == LOADER_PROVISIONING_VALUES) {
+ mProvisioningValues = values;
+ mEditPackageName.setText(values.get(
+ DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME));
+ mEditLocale.setText(values.get(DevicePolicyManager.EXTRA_PROVISIONING_LOCALE));
+ mEditTimezone.setText(values.get(DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE));
+ mEditWifiSsid.setText(values.get(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID));
+ mEditWifiSecurityType.setText(values.get(
+ DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SECURITY_TYPE));
+ mEditWifiPassword.setText(null);
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Map<String, String>> loader) {
+ // Do nothing
+ }
+
+}
diff --git a/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/ProvisioningValuesLoader.java b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/ProvisioningValuesLoader.java
new file mode 100644
index 0000000..2203c30
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/ProvisioningValuesLoader.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+package com.example.android.nfcprovisioning;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Environment;
+import android.support.v4.content.AsyncTaskLoader;
+
+import com.example.android.common.logger.Log;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * Loads default values for NFC provisioning.
+ * <p/>
+ * This loader first tries to load values from a config file in SD card. Then it fills in missing
+ * values using constants and settings on the programming device.
+ */
+public class ProvisioningValuesLoader extends AsyncTaskLoader<Map<String, String>> {
+
+ private static final String FILENAME = "nfcprovisioning.txt";
+ private static final String TAG = "LoadProvisioningValuesTask";
+
+ private Map<String, String> mValues;
+
+ public ProvisioningValuesLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Map<String, String> loadInBackground() {
+ HashMap<String, String> values = new HashMap<>();
+ loadFromDisk(values);
+ loadSystemValues(values);
+ return values;
+ }
+
+ @Override
+ public void deliverResult(Map<String, String> values) {
+ if (isReset()) {
+ return;
+ }
+ mValues = values;
+ super.deliverResult(values);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mValues != null) {
+ deliverResult(mValues);
+ }
+ if (takeContentChanged() || mValues == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ onStopLoading();
+ mValues = null;
+ }
+
+ private void loadFromDisk(HashMap<String, String> values) {
+ File directory = Environment.getExternalStorageDirectory();
+ File file = new File(directory, FILENAME);
+ if (!file.exists()) {
+ return;
+ }
+ Log.d(TAG, "Loading the config file...");
+ try {
+ loadFromFile(values, file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.e(TAG, "Error loading data from " + file, e);
+ }
+ }
+
+ private void loadFromFile(HashMap<String, String> values, File file) throws IOException {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(file));
+ String line;
+ while (null != (line = reader.readLine())) {
+ if (line.startsWith("#")) {
+ continue;
+ }
+ int position = line.indexOf("=");
+ if (position < 0) { // Not found
+ continue;
+ }
+ String key = line.substring(0, position);
+ String value = line.substring(position + 1);
+ values.put(key, value);
+ Log.d(TAG, key + "=" + value);
+ }
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+ }
+
+ private void loadSystemValues(HashMap<String, String> values) {
+ Context context = getContext();
+ putIfMissing(values, DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
+ "com.example.android.deviceowner");
+ putIfMissing(values, DevicePolicyManager.EXTRA_PROVISIONING_LOCALE,
+ context.getResources().getConfiguration().locale.toString());
+ putIfMissing(values, DevicePolicyManager.EXTRA_PROVISIONING_TIME_ZONE,
+ TimeZone.getDefault().getID());
+ if (!values.containsKey(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID)) {
+ WifiManager wifiManager = (WifiManager) context
+ .getSystemService(Activity.WIFI_SERVICE);
+ WifiInfo info = wifiManager.getConnectionInfo();
+ values.put(DevicePolicyManager.EXTRA_PROVISIONING_WIFI_SSID, trimSsid(info.getSSID()));
+ }
+ }
+
+ /**
+ * {@link WifiInfo#getSSID} returns the WiFi SSID surrounded by double quotation marks. This
+ * method removes them if wifiSsid contains them.
+ */
+ private static String trimSsid(String wifiSsid) {
+ int head = wifiSsid.startsWith("\"") ? 1 : 0;
+ int tail = wifiSsid.endsWith("\"") ? 1 : 0;
+ return wifiSsid.substring(head, wifiSsid.length() - tail);
+ }
+
+ private static <Key, Value> void putIfMissing(HashMap<Key, Value> map, Key key, Value value) {
+ if (!map.containsKey(key)) {
+ map.put(key, value);
+ }
+ }
+
+}
diff --git a/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/TextWatcherWrapper.java b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/TextWatcherWrapper.java
new file mode 100644
index 0000000..d36936e
--- /dev/null
+++ b/samples/browseable/NfcProvisioning/src/com.example.android.nfcprovisioning/TextWatcherWrapper.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package com.example.android.nfcprovisioning;
+
+import android.text.Editable;
+import android.text.TextWatcher;
+
+/**
+ * This class wraps {@link TextWatcher} and delegates {@link TextWatcher#afterTextChanged} event to
+ * {@link OnTextChangedListener} while hiding all other unnecessary events.
+ */
+public class TextWatcherWrapper implements TextWatcher {
+
+ private final int mId;
+ private final OnTextChangedListener mListener;
+
+ public TextWatcherWrapper(int id, OnTextChangedListener listener) {
+ mId = id;
+ mListener = listener;
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Ignore
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Ignore
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ mListener.onTextChanged(mId, s.toString());
+ }
+
+ public interface OnTextChangedListener {
+ public void onTextChanged(int id, String s);
+ }
+
+}
diff --git a/samples/browseable/Notifications/Application/res/layout/activity_main.xml b/samples/browseable/Notifications/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/Notifications/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/PdfRendererBasic/res/layout/activity_main.xml b/samples/browseable/PdfRendererBasic/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/PdfRendererBasic/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/PermissionRequest/AndroidManifest.xml b/samples/browseable/PermissionRequest/AndroidManifest.xml
new file mode 100644
index 0000000..f8fd050
--- /dev/null
+++ b/samples/browseable/PermissionRequest/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2014 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.permissionrequest"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+
+ <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/PermissionRequest/_index.jd b/samples/browseable/PermissionRequest/_index.jd
new file mode 100644
index 0000000..d0f6785
--- /dev/null
+++ b/samples/browseable/PermissionRequest/_index.jd
@@ -0,0 +1,10 @@
+page.tags="PermissionRequest"
+sample.group=Content
+@jd:body
+
+<p>
+
+ This sample shows how to handle PermissionRequest coming from web apps inside of a
+ WebView.
+
+ </p>
diff --git a/samples/browseable/PermissionRequest/res/drawable-hdpi/ic_launcher.png b/samples/browseable/PermissionRequest/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..4dae25b
--- /dev/null
+++ b/samples/browseable/PermissionRequest/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/PermissionRequest/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
copy to samples/browseable/PermissionRequest/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/PermissionRequest/res/drawable-mdpi/ic_launcher.png b/samples/browseable/PermissionRequest/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..e896d49
--- /dev/null
+++ b/samples/browseable/PermissionRequest/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/PermissionRequest/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/PermissionRequest/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..5309f55
--- /dev/null
+++ b/samples/browseable/PermissionRequest/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/PermissionRequest/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/PermissionRequest/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..61b137b
--- /dev/null
+++ b/samples/browseable/PermissionRequest/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/PermissionRequest/res/layout-w720dp/activity_main.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml
copy to samples/browseable/PermissionRequest/res/layout-w720dp/activity_main.xml
diff --git a/samples/browseable/AdapterTransition/res/layout/activity_main.xml b/samples/browseable/PermissionRequest/res/layout/activity_main.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/layout/activity_main.xml
copy to samples/browseable/PermissionRequest/res/layout/activity_main.xml
diff --git a/samples/browseable/PermissionRequest/res/layout/fragment_permission_request.xml b/samples/browseable/PermissionRequest/res/layout/fragment_permission_request.xml
new file mode 100644
index 0000000..2e97a0a
--- /dev/null
+++ b/samples/browseable/PermissionRequest/res/layout/fragment_permission_request.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <WebView
+ android:id="@+id/web_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/samples/browseable/AdapterTransition/res/menu/main.xml b/samples/browseable/PermissionRequest/res/menu/main.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/menu/main.xml
copy to samples/browseable/PermissionRequest/res/menu/main.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/PermissionRequest/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/PermissionRequest/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/PermissionRequest/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
copy to samples/browseable/PermissionRequest/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/PermissionRequest/res/values-v11/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
copy to samples/browseable/PermissionRequest/res/values-v11/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-colors.xml b/samples/browseable/PermissionRequest/res/values-v21/base-colors.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-colors.xml
copy to samples/browseable/PermissionRequest/res/values-v21/base-colors.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml b/samples/browseable/PermissionRequest/res/values-v21/base-template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml
copy to samples/browseable/PermissionRequest/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml b/samples/browseable/PermissionRequest/res/values/base-strings.xml
similarity index 81%
rename from samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
rename to samples/browseable/PermissionRequest/res/values/base-strings.xml
index d653382..c66a246 100644
--- a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
+++ b/samples/browseable/PermissionRequest/res/values/base-strings.xml
@@ -15,12 +15,13 @@
limitations under the License.
-->
<resources>
- <string name="app_name">WatchViewStub</string>
+ <string name="app_name">PermissionRequest</string>
<string name="intro_message">
<![CDATA[
- This sample demonstrates how to specify different layouts for round and rectangular screens.
+ This sample shows how to handle PermissionRequest coming from web apps inside of a
+ WebView.
]]>
diff --git a/samples/browseable/AdapterTransition/res/values/fragmentview_strings.xml b/samples/browseable/PermissionRequest/res/values/fragmentview_strings.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/fragmentview_strings.xml
copy to samples/browseable/PermissionRequest/res/values/fragmentview_strings.xml
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml b/samples/browseable/PermissionRequest/res/values/strings.xml
similarity index 68%
copy from samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
copy to samples/browseable/PermissionRequest/res/values/strings.xml
index 34c9cd1..c3e5574 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v21/base-colors.xml
+++ b/samples/browseable/PermissionRequest/res/values/strings.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2013 The Android Open Source Project
+ Copyright 2014 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.
@@ -15,6 +15,7 @@
limitations under the License.
-->
<resources>
-
-
+ <string name="confirmation">This web page wants to use following resources:\n\n%s</string>
+ <string name="allow">Allow</string>
+ <string name="deny">Deny</string>
</resources>
diff --git a/samples/browseable/AdapterTransition/res/values/template-dimens.xml b/samples/browseable/PermissionRequest/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-dimens.xml
copy to samples/browseable/PermissionRequest/res/values/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values/template-styles.xml b/samples/browseable/PermissionRequest/res/values/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-styles.xml
copy to samples/browseable/PermissionRequest/res/values/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/PermissionRequest/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/PermissionRequest/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/PermissionRequest/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
copy to samples/browseable/PermissionRequest/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/PermissionRequest/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/PermissionRequest/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/PermissionRequest/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/ConfirmationDialogFragment.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/ConfirmationDialogFragment.java
new file mode 100644
index 0000000..7dae56e
--- /dev/null
+++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/ConfirmationDialogFragment.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 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.permissionrequest;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.text.TextUtils;
+
+/**
+ * Prompts the user to confirm permission request.
+ */
+public class ConfirmationDialogFragment extends DialogFragment {
+
+ private static final String ARG_RESOURCES = "resources";
+
+ /**
+ * Creates a new instance of ConfirmationDialogFragment.
+ *
+ * @param resources The list of resources requested by PermissionRequeste.
+ * @return A new instance.
+ */
+ public static ConfirmationDialogFragment newInstance(String[] resources) {
+ ConfirmationDialogFragment fragment = new ConfirmationDialogFragment();
+ Bundle args = new Bundle();
+ args.putStringArray(ARG_RESOURCES, resources);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ String[] resources = getArguments().getStringArray(ARG_RESOURCES);
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(getString(R.string.confirmation, TextUtils.join("\n", resources)))
+ .setNegativeButton(R.string.deny, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ((Listener) getParentFragment()).onConfirmation(false);
+ }
+ })
+ .setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ((Listener) getParentFragment()).onConfirmation(true);
+ }
+ })
+ .create();
+ }
+
+ /**
+ * Callback for the user's response.
+ */
+ public interface Listener {
+
+ /**
+ * Called when the PermissoinRequest is allowed or denied by the user.
+ *
+ * @param allowed True if the user allowed the request.
+ */
+ public void onConfirmation(boolean allowed);
+ }
+
+}
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/MainActivity.java
similarity index 96%
copy from samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
copy to samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/MainActivity.java
index 0adb842..a11849f 100644
--- a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
+++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/MainActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.adaptertransition;
+package com.example.android.permissionrequest;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
@@ -49,7 +49,7 @@
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- AdapterTransitionFragment fragment = new AdapterTransitionFragment();
+ PermissionRequestFragment fragment = new PermissionRequestFragment();
transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();
}
diff --git a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/PermissionRequestFragment.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/PermissionRequestFragment.java
new file mode 100644
index 0000000..44f1d6e
--- /dev/null
+++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/PermissionRequestFragment.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 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.permissionrequest;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.ConsoleMessage;
+import android.webkit.PermissionRequest;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+import com.example.android.common.logger.Log;
+
+/**
+ * This fragment shows a {@link WebView} and loads a web app from the {@link SimpleWebServer}.
+ */
+public class PermissionRequestFragment extends Fragment
+ implements ConfirmationDialogFragment.Listener {
+
+ private static final String TAG = PermissionRequestFragment.class.getSimpleName();
+
+ private static final String FRAGMENT_DIALOG = "dialog";
+
+ /**
+ * We use this web server to serve HTML files in the assets folder. This is because we cannot
+ * use the JavaScript method "getUserMedia" from "file:///android_assets/..." URLs.
+ */
+ private SimpleWebServer mWebServer;
+
+ /**
+ * A reference to the {@link WebView}.
+ */
+ private WebView mWebView;
+
+ /**
+ * This field stores the {@link PermissionRequest} from the web application until it is allowed
+ * or denied by user.
+ */
+ private PermissionRequest mPermissionRequest;
+
+ /**
+ * For testing.
+ */
+ private ConsoleMonitor mConsoleMonitor;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_permission_request, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ mWebView = (WebView) view.findViewById(R.id.web_view);
+ // Here, we use #mWebChromeClient with implementation for handling PermissionRequests.
+ mWebView.setWebChromeClient(mWebChromeClient);
+ configureWebSettings(mWebView.getSettings());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ final int port = 8080;
+ mWebServer = new SimpleWebServer(port, getResources().getAssets());
+ mWebServer.start();
+ mWebView.loadUrl("http://localhost:" + port + "/sample.html");
+ }
+
+ @Override
+ public void onPause() {
+ mWebServer.stop();
+ super.onPause();
+ }
+
+ @SuppressLint("SetJavaScriptEnabled")
+ private static void configureWebSettings(WebSettings settings) {
+ settings.setJavaScriptEnabled(true);
+ }
+
+ /**
+ * This {@link WebChromeClient} has implementation for handling {@link PermissionRequest}.
+ */
+ private WebChromeClient mWebChromeClient = new WebChromeClient() {
+
+ // This method is called when the web content is requesting permission to access some
+ // resources.
+ @Override
+ public void onPermissionRequest(PermissionRequest request) {
+ Log.i(TAG, "onPermissionRequest");
+ mPermissionRequest = request;
+ ConfirmationDialogFragment.newInstance(request.getResources())
+ .show(getChildFragmentManager(), FRAGMENT_DIALOG);
+ }
+
+ // This method is called when the permission request is canceled by the web content.
+ @Override
+ public void onPermissionRequestCanceled(PermissionRequest request) {
+ Log.i(TAG, "onPermissionRequestCanceled");
+ // We dismiss the prompt UI here as the request is no longer valid.
+ mPermissionRequest = null;
+ DialogFragment fragment = (DialogFragment) getChildFragmentManager()
+ .findFragmentByTag(FRAGMENT_DIALOG);
+ if (null != fragment) {
+ fragment.dismiss();
+ }
+ }
+
+ @Override
+ public boolean onConsoleMessage(@NonNull ConsoleMessage message) {
+ switch (message.messageLevel()) {
+ case TIP:
+ Log.v(TAG, message.message());
+ break;
+ case LOG:
+ Log.i(TAG, message.message());
+ break;
+ case WARNING:
+ Log.w(TAG, message.message());
+ break;
+ case ERROR:
+ Log.e(TAG, message.message());
+ break;
+ case DEBUG:
+ Log.d(TAG, message.message());
+ break;
+ }
+ if (null != mConsoleMonitor) {
+ mConsoleMonitor.onConsoleMessage(message);
+ }
+ return true;
+ }
+
+ };
+
+ @Override
+ public void onConfirmation(boolean allowed) {
+ if (allowed) {
+ mPermissionRequest.grant(mPermissionRequest.getResources());
+ Log.d(TAG, "Permission granted.");
+ } else {
+ mPermissionRequest.deny();
+ Log.d(TAG, "Permission request denied.");
+ }
+ mPermissionRequest = null;
+ }
+
+ public void setConsoleMonitor(ConsoleMonitor monitor) {
+ mConsoleMonitor = monitor;
+ }
+
+ /**
+ * For testing.
+ */
+ public interface ConsoleMonitor {
+ public void onConsoleMessage(ConsoleMessage message);
+ }
+
+}
diff --git a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/SimpleWebServer.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/SimpleWebServer.java
new file mode 100644
index 0000000..36b7c46
--- /dev/null
+++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/SimpleWebServer.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 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.permissionrequest;
+
+import android.content.res.AssetManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+/**
+ * Implementation of a very basic HTTP server. The contents are loaded from the assets folder. This
+ * server handles one request at a time. It only supports GET method.
+ */
+public class SimpleWebServer implements Runnable {
+
+ private static final String TAG = "SimpleWebServer";
+
+ /**
+ * The port number we listen to
+ */
+ private final int mPort;
+
+ /**
+ * {@link android.content.res.AssetManager} for loading files to serve.
+ */
+ private final AssetManager mAssets;
+
+ /**
+ * True if the server is running.
+ */
+ private boolean mIsRunning;
+
+ /**
+ * The {@link java.net.ServerSocket} that we listen to.
+ */
+ private ServerSocket mServerSocket;
+
+ /**
+ * WebServer constructor.
+ */
+ public SimpleWebServer(int port, AssetManager assets) {
+ mPort = port;
+ mAssets = assets;
+ }
+
+ /**
+ * This method starts the web server listening to the specified port.
+ */
+ public void start() {
+ mIsRunning = true;
+ new Thread(this).start();
+ }
+
+ /**
+ * This method stops the web server
+ */
+ public void stop() {
+ try {
+ mIsRunning = false;
+ if (null != mServerSocket) {
+ mServerSocket.close();
+ mServerSocket = null;
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing the server socket.", e);
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ mServerSocket = new ServerSocket(mPort);
+ while (mIsRunning) {
+ Socket socket = mServerSocket.accept();
+ handle(socket);
+ socket.close();
+ }
+ } catch (SocketException e) {
+ // The server was stopped; ignore.
+ } catch (IOException e) {
+ Log.e(TAG, "Web server error.", e);
+ }
+ }
+
+ /**
+ * Respond to a request from a client.
+ *
+ * @param socket The client socket.
+ * @throws IOException
+ */
+ private void handle(Socket socket) throws IOException {
+ BufferedReader reader = null;
+ PrintStream output = null;
+ try {
+ String route = null;
+
+ // Read HTTP headers and parse out the route.
+ reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ String line;
+ while (!TextUtils.isEmpty(line = reader.readLine())) {
+ if (line.startsWith("GET /")) {
+ int start = line.indexOf('/') + 1;
+ int end = line.indexOf(' ', start);
+ route = line.substring(start, end);
+ break;
+ }
+ }
+
+ // Output stream that we send the response to
+ output = new PrintStream(socket.getOutputStream());
+
+ // Prepare the content to send.
+ if (null == route) {
+ writeServerError(output);
+ return;
+ }
+ byte[] bytes = loadContent(route);
+ if (null == bytes) {
+ writeServerError(output);
+ return;
+ }
+
+ // Send out the content.
+ output.println("HTTP/1.0 200 OK");
+ output.println("Content-Type: " + detectMimeType(route));
+ output.println("Content-Length: " + bytes.length);
+ output.println();
+ output.write(bytes);
+ output.flush();
+ } finally {
+ if (null != output) {
+ output.close();
+ }
+ if (null != reader) {
+ reader.close();
+ }
+ }
+ }
+
+ /**
+ * Writes a server error response (HTTP/1.0 500) to the given output stream.
+ *
+ * @param output The output stream.
+ */
+ private void writeServerError(PrintStream output) {
+ output.println("HTTP/1.0 500 Internal Server Error");
+ output.flush();
+ }
+
+ /**
+ * Loads all the content of {@code fileName}.
+ *
+ * @param fileName The name of the file.
+ * @return The content of the file.
+ * @throws IOException
+ */
+ private byte[] loadContent(String fileName) throws IOException {
+ InputStream input = null;
+ try {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ input = mAssets.open(fileName);
+ byte[] buffer = new byte[1024];
+ int size;
+ while (-1 != (size = input.read(buffer))) {
+ output.write(buffer, 0, size);
+ }
+ output.flush();
+ return output.toByteArray();
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ if (null != input) {
+ input.close();
+ }
+ }
+ }
+
+ /**
+ * Detects the MIME type from the {@code fileName}.
+ *
+ * @param fileName The name of the file.
+ * @return A MIME type.
+ */
+ private String detectMimeType(String fileName) {
+ if (TextUtils.isEmpty(fileName)) {
+ return null;
+ } else if (fileName.endsWith(".html")) {
+ return "text/html";
+ } else if (fileName.endsWith(".js")) {
+ return "application/javascript";
+ } else if (fileName.endsWith(".css")) {
+ return "text/css";
+ } else {
+ return "application/octet-stream";
+ }
+ }
+
+}
diff --git a/samples/browseable/Quiz/Application/res/layout/activity_main.xml b/samples/browseable/Quiz/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/Quiz/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/RecipeAssistant/Application/res/layout/activity_main.xml b/samples/browseable/RecipeAssistant/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/RecipeAssistant/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/RecyclerView/src/com.example.android.recyclerview/RecyclerViewFragment.java b/samples/browseable/RecyclerView/src/com.example.android.recyclerview/RecyclerViewFragment.java
index 019657f..1c0fded 100644
--- a/samples/browseable/RecyclerView/src/com.example.android.recyclerview/RecyclerViewFragment.java
+++ b/samples/browseable/RecyclerView/src/com.example.android.recyclerview/RecyclerViewFragment.java
@@ -80,7 +80,7 @@
if (savedInstanceState != null) {
// Restore saved layout manager type.
mCurrentLayoutManagerType = (LayoutManagerType) savedInstanceState
- .getSerializable(LAYOUT_MANAGER_KEY);
+ .getSerializable(KEY_LAYOUT_MANAGER);
}
setRecyclerViewLayoutManager(mCurrentLayoutManagerType);
@@ -143,7 +143,7 @@
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save currently selected layout manager.
- savedInstanceState.putSerializable(LAYOUT_MANAGER_KEY, mCurrentLayoutManagerType);
+ savedInstanceState.putSerializable(KEY_LAYOUT_MANAGER, mCurrentLayoutManagerType);
super.onSaveInstanceState(savedInstanceState);
}
diff --git a/samples/browseable/RenderScriptIntrinsic/res/layout/activity_main.xml b/samples/browseable/RenderScriptIntrinsic/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/RenderScriptIntrinsic/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/RevealEffectBasic/res/drawable-hdpi/ic_launcher.png b/samples/browseable/RevealEffectBasic/res/drawable-hdpi/ic_launcher.png
index 5acc60a..f5357ea 100644
--- a/samples/browseable/RevealEffectBasic/res/drawable-hdpi/ic_launcher.png
+++ b/samples/browseable/RevealEffectBasic/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/RevealEffectBasic/res/drawable-mdpi/ic_launcher.png b/samples/browseable/RevealEffectBasic/res/drawable-mdpi/ic_launcher.png
index d3977a6..de29888 100644
--- a/samples/browseable/RevealEffectBasic/res/drawable-mdpi/ic_launcher.png
+++ b/samples/browseable/RevealEffectBasic/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/RevealEffectBasic/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/RevealEffectBasic/res/drawable-xhdpi/ic_launcher.png
index 71532a7..cfdb7c0 100644
--- a/samples/browseable/RevealEffectBasic/res/drawable-xhdpi/ic_launcher.png
+++ b/samples/browseable/RevealEffectBasic/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/RevealEffectBasic/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/RevealEffectBasic/res/drawable-xxhdpi/ic_launcher.png
index 33d9879..87993b5 100644
--- a/samples/browseable/RevealEffectBasic/res/drawable-xxhdpi/ic_launcher.png
+++ b/samples/browseable/RevealEffectBasic/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/GridViewPager/Application/AndroidManifest.xml b/samples/browseable/ScreenCapture/AndroidManifest.xml
similarity index 64%
rename from samples/browseable/GridViewPager/Application/AndroidManifest.xml
rename to samples/browseable/ScreenCapture/AndroidManifest.xml
index b6092c5..2dfd4f7 100644
--- a/samples/browseable/GridViewPager/Application/AndroidManifest.xml
+++ b/samples/browseable/ScreenCapture/AndroidManifest.xml
@@ -14,17 +14,21 @@
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.gridviewpager">
-
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="21" />
+ package="com.example.android.screencapture">
<application android:allowBackup="true"
+ android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
+ 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/ScreenCapture/_index.jd b/samples/browseable/ScreenCapture/_index.jd
new file mode 100644
index 0000000..ee22e4b
--- /dev/null
+++ b/samples/browseable/ScreenCapture/_index.jd
@@ -0,0 +1,10 @@
+page.tags="ScreenCapture"
+sample.group=Media
+@jd:body
+
+<p>
+
+This sample demonstrates how to use Media Projection API introduced in Android 5.0 Lollipop. Press
+"Start" to start capturing the screen.
+
+ </p>
diff --git a/samples/browseable/ScreenCapture/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ScreenCapture/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..69d518c
--- /dev/null
+++ b/samples/browseable/ScreenCapture/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png b/samples/browseable/ScreenCapture/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/AdapterTransition/res/drawable-hdpi/tile.9.png
copy to samples/browseable/ScreenCapture/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/ScreenCapture/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ScreenCapture/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c107f97
--- /dev/null
+++ b/samples/browseable/ScreenCapture/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ScreenCapture/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ScreenCapture/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..a4d33c2
--- /dev/null
+++ b/samples/browseable/ScreenCapture/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/ScreenCapture/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ScreenCapture/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..fe23a07
--- /dev/null
+++ b/samples/browseable/ScreenCapture/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml b/samples/browseable/ScreenCapture/res/layout-w720dp/activity_main.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/layout-w720dp/activity_main.xml
copy to samples/browseable/ScreenCapture/res/layout-w720dp/activity_main.xml
diff --git a/samples/browseable/AdapterTransition/res/layout/activity_main.xml b/samples/browseable/ScreenCapture/res/layout/activity_main.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/layout/activity_main.xml
copy to samples/browseable/ScreenCapture/res/layout/activity_main.xml
diff --git a/samples/browseable/ScreenCapture/res/layout/fragment_screen_capture.xml b/samples/browseable/ScreenCapture/res/layout/fragment_screen_capture.xml
new file mode 100644
index 0000000..343c920
--- /dev/null
+++ b/samples/browseable/ScreenCapture/res/layout/fragment_screen_capture.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2014 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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <SurfaceView
+ android:id="@+id/surface"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:text="@string/start"/>
+
+</LinearLayout>
diff --git a/samples/browseable/AdapterTransition/res/menu/main.xml b/samples/browseable/ScreenCapture/res/menu/main.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/menu/main.xml
copy to samples/browseable/ScreenCapture/res/menu/main.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml b/samples/browseable/ScreenCapture/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/ScreenCapture/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml b/samples/browseable/ScreenCapture/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-sw600dp/template-styles.xml
copy to samples/browseable/ScreenCapture/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v11/template-styles.xml b/samples/browseable/ScreenCapture/res/values-v11/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v11/template-styles.xml
copy to samples/browseable/ScreenCapture/res/values-v11/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-colors.xml b/samples/browseable/ScreenCapture/res/values-v21/base-colors.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-colors.xml
copy to samples/browseable/ScreenCapture/res/values-v21/base-colors.xml
diff --git a/samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml b/samples/browseable/ScreenCapture/res/values-v21/base-template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values-v21/base-template-styles.xml
copy to samples/browseable/ScreenCapture/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml b/samples/browseable/ScreenCapture/res/values/base-strings.xml
similarity index 79%
copy from samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
copy to samples/browseable/ScreenCapture/res/values/base-strings.xml
index d653382..79880f0 100644
--- a/samples/browseable/WatchViewStub/Application/res/values/base-strings.xml
+++ b/samples/browseable/ScreenCapture/res/values/base-strings.xml
@@ -15,13 +15,14 @@
limitations under the License.
-->
<resources>
- <string name="app_name">WatchViewStub</string>
+ <string name="app_name">ScreenCapture</string>
<string name="intro_message">
<![CDATA[
-
- This sample demonstrates how to specify different layouts for round and rectangular screens.
-
+
+This sample demonstrates how to use Media Projection API introduced in Android 5.0 Lollipop. Press
+"Start" to start capturing the screen.
+
]]>
</string>
diff --git a/samples/browseable/AdapterTransition/res/values/fragmentview_strings.xml b/samples/browseable/ScreenCapture/res/values/fragmentview_strings.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/fragmentview_strings.xml
copy to samples/browseable/ScreenCapture/res/values/fragmentview_strings.xml
diff --git a/samples/browseable/AdapterTransition/res/values/strings.xml b/samples/browseable/ScreenCapture/res/values/strings.xml
similarity index 79%
rename from samples/browseable/AdapterTransition/res/values/strings.xml
rename to samples/browseable/ScreenCapture/res/values/strings.xml
index 02b87cf..70597e6 100644
--- a/samples/browseable/AdapterTransition/res/values/strings.xml
+++ b/samples/browseable/ScreenCapture/res/values/strings.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
<resources>
- <string name="item_clicked">%s clicked</string>
- <string name="show_as_grid">Show as grid</string>
- <string name="show_as_list">Show as list</string>
+ <string name="start">Start</string>
+ <string name="stop">Stop</string>
+ <string name="user_cancelled">User denied screen sharing permission</string>
</resources>
diff --git a/samples/browseable/AdapterTransition/res/values/template-dimens.xml b/samples/browseable/ScreenCapture/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-dimens.xml
copy to samples/browseable/ScreenCapture/res/values/template-dimens.xml
diff --git a/samples/browseable/AdapterTransition/res/values/template-styles.xml b/samples/browseable/ScreenCapture/res/values/template-styles.xml
similarity index 100%
copy from samples/browseable/AdapterTransition/res/values/template-styles.xml
copy to samples/browseable/ScreenCapture/res/values/template-styles.xml
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java b/samples/browseable/ScreenCapture/src/com.example.android.common/activities/SampleActivityBase.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/activities/SampleActivityBase.java
copy to samples/browseable/ScreenCapture/src/com.example.android.common/activities/SampleActivityBase.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java b/samples/browseable/ScreenCapture/src/com.example.android.common/logger/Log.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/Log.java
copy to samples/browseable/ScreenCapture/src/com.example.android.common/logger/Log.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java b/samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogFragment.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogFragment.java
copy to samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogFragment.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java b/samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogNode.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogNode.java
copy to samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogNode.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java b/samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogView.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogView.java
copy to samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogView.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java b/samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogWrapper.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/LogWrapper.java
copy to samples/browseable/ScreenCapture/src/com.example.android.common/logger/LogWrapper.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java b/samples/browseable/ScreenCapture/src/com.example.android.common/logger/MessageOnlyLogFilter.java
similarity index 100%
copy from samples/browseable/AdapterTransition/src/com.example.android.common/logger/MessageOnlyLogFilter.java
copy to samples/browseable/ScreenCapture/src/com.example.android.common/logger/MessageOnlyLogFilter.java
diff --git a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java b/samples/browseable/ScreenCapture/src/com.example.android.screencapture/MainActivity.java
similarity index 96%
copy from samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
copy to samples/browseable/ScreenCapture/src/com.example.android.screencapture/MainActivity.java
index 0adb842..67ae9e8 100644
--- a/samples/browseable/AdapterTransition/src/com.example.android.adaptertransition/MainActivity.java
+++ b/samples/browseable/ScreenCapture/src/com.example.android.screencapture/MainActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.adaptertransition;
+package com.example.android.screencapture;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
@@ -49,7 +49,7 @@
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- AdapterTransitionFragment fragment = new AdapterTransitionFragment();
+ ScreenCaptureFragment fragment = new ScreenCaptureFragment();
transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();
}
diff --git a/samples/browseable/ScreenCapture/src/com.example.android.screencapture/ScreenCaptureFragment.java b/samples/browseable/ScreenCapture/src/com.example.android.screencapture/ScreenCaptureFragment.java
new file mode 100644
index 0000000..f134579
--- /dev/null
+++ b/samples/browseable/ScreenCapture/src/com.example.android.screencapture/ScreenCaptureFragment.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2014 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.screencapture;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionManager;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.example.android.common.logger.Log;
+
+/**
+ * Provides UI for the screen capture.
+ */
+public class ScreenCaptureFragment extends Fragment implements View.OnClickListener {
+
+ private static final String TAG = "ScreenCaptureFragment";
+
+ private static final String STATE_RESULT_CODE = "result_code";
+ private static final String STATE_RESULT_DATA = "result_data";
+
+ private static final int REQUEST_MEDIA_PROJECTION = 1;
+
+ private int mScreenDensity;
+
+ private int mResultCode;
+ private Intent mResultData;
+
+ private Surface mSurface;
+ private MediaProjection mMediaProjection;
+ private VirtualDisplay mVirtualDisplay;
+ private MediaProjectionManager mMediaProjectionManager;
+ private Button mButtonToggle;
+ private SurfaceView mSurfaceView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mResultCode = savedInstanceState.getInt(STATE_RESULT_CODE);
+ mResultData = savedInstanceState.getParcelable(STATE_RESULT_DATA);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_screen_capture, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ mSurfaceView = (SurfaceView) view.findViewById(R.id.surface);
+ mSurface = mSurfaceView.getHolder().getSurface();
+ mButtonToggle = (Button) view.findViewById(R.id.toggle);
+ mButtonToggle.setOnClickListener(this);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Activity activity = getActivity();
+ DisplayMetrics metrics = new DisplayMetrics();
+ activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ mScreenDensity = metrics.densityDpi;
+ mMediaProjectionManager = (MediaProjectionManager)
+ activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mResultData != null) {
+ outState.putInt(STATE_RESULT_CODE, mResultCode);
+ outState.putParcelable(STATE_RESULT_DATA, mResultData);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.toggle:
+ if (mVirtualDisplay == null) {
+ startScreenCapture();
+ } else {
+ stopScreenCapture();
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_MEDIA_PROJECTION) {
+ if (resultCode != Activity.RESULT_OK) {
+ Log.i(TAG, "User cancelled");
+ Toast.makeText(getActivity(), R.string.user_cancelled, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ Log.i(TAG, "Starting screen capture");
+ mResultCode = resultCode;
+ mResultData = data;
+ setUpMediaProjection();
+ setUpVirtualDisplay();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ stopScreenCapture();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ tearDownMediaProjection();
+ }
+
+ private void setUpMediaProjection() {
+ mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
+ }
+
+ private void tearDownMediaProjection() {
+ if (mMediaProjection != null) {
+ mMediaProjection.stop();
+ mMediaProjection = null;
+ }
+ }
+
+ private void startScreenCapture() {
+ Activity activity = getActivity();
+ if (mSurface == null || activity == null) {
+ return;
+ }
+ if (mMediaProjection != null) {
+ setUpVirtualDisplay();
+ } else if (mResultCode != 0 && mResultData != null) {
+ setUpMediaProjection();
+ setUpVirtualDisplay();
+ } else {
+ Log.i(TAG, "Requesting confirmation");
+ // This initiates a prompt dialog for the user to confirm screen projection.
+ startActivityForResult(
+ mMediaProjectionManager.createScreenCaptureIntent(),
+ REQUEST_MEDIA_PROJECTION);
+ }
+ }
+
+ private void setUpVirtualDisplay() {
+ Log.i(TAG, "Setting up a VirtualDisplay: " +
+ mSurfaceView.getWidth() + "x" + mSurfaceView.getHeight() +
+ " (" + mScreenDensity + ")");
+ mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
+ mSurfaceView.getWidth(), mSurfaceView.getHeight(), mScreenDensity,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ mSurface, null, null);
+ mButtonToggle.setText(R.string.stop);
+ }
+
+ private void stopScreenCapture() {
+ if (mVirtualDisplay == null) {
+ return;
+ }
+ mVirtualDisplay.release();
+ mVirtualDisplay = null;
+ mButtonToggle.setText(R.string.start);
+ }
+
+}
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/AndroidManifest.xml b/samples/browseable/SkeletonWearableApp/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/AndroidManifest.xml
rename to samples/browseable/SkeletonWearableApp/AndroidManifest.xml
diff --git a/samples/browseable/SkeletonWearableApp/Application/AndroidManifest.xml b/samples/browseable/SkeletonWearableApp/Application/AndroidManifest.xml
deleted file mode 100644
index 23497ca..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2014 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.google.wearable.app">
-
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="21" />
-
- <application android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
- </application>
-
-</manifest>
-
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/Application/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/SkeletonWearableApp/Application/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/drawable-mdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/Application/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/Application/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/Application/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/layout/activity_main.xml b/samples/browseable/SkeletonWearableApp/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/SkeletonWearableApp/Application/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- 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/SkeletonWearableApp/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/SkeletonWearableApp/Application/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/SkeletonWearableApp/Application/res/values-v11/template-styles.xml b/samples/browseable/SkeletonWearableApp/Application/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- 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/SkeletonWearableApp/Application/res/values-v21/base-colors.xml b/samples/browseable/SkeletonWearableApp/Application/res/values-v21/base-colors.xml
deleted file mode 100644
index 34c9cd1..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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>
-
-
-</resources>
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/values-v21/base-template-styles.xml b/samples/browseable/SkeletonWearableApp/Application/res/values-v21/base-template-styles.xml
deleted file mode 100644
index 0b2948f..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
- </style>
-
-</resources>
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/values/base-strings.xml b/samples/browseable/SkeletonWearableApp/Application/res/values/base-strings.xml
deleted file mode 100644
index 6bdffc6..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values/base-strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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">SkeletonWearableApp</string>
- <string name="intro_message">
- <![CDATA[
-
-
- This sample is a mostly empty wearable app that implements a fullscreen activity
- conforming to Android Wear best practices. Included in the sample are examples of GridViewPager,
- DelayedConfirmationView, and DismissOverlayView. Developers who require a fullscreen activity for
- their wearable app can use this sample as a starting point.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/SkeletonWearableApp/Application/res/values/template-dimens.xml b/samples/browseable/SkeletonWearableApp/Application/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/SkeletonWearableApp/Application/res/values/template-styles.xml b/samples/browseable/SkeletonWearableApp/Application/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593..0000000
--- a/samples/browseable/SkeletonWearableApp/Application/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- 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/SkeletonWearableApp/Wearable/res/drawable-hdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/SkeletonWearableApp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/res/drawable-mdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/SkeletonWearableApp/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/SkeletonWearableApp/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/SkeletonWearableApp/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/SkeletonWearableApp/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/res/layout/grid_activity.xml b/samples/browseable/SkeletonWearableApp/res/layout/grid_activity.xml
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/layout/grid_activity.xml
rename to samples/browseable/SkeletonWearableApp/res/layout/grid_activity.xml
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/res/layout/main_activity.xml b/samples/browseable/SkeletonWearableApp/res/layout/main_activity.xml
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/layout/main_activity.xml
rename to samples/browseable/SkeletonWearableApp/res/layout/main_activity.xml
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/res/values/dimens.xml b/samples/browseable/SkeletonWearableApp/res/values/dimens.xml
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/values/dimens.xml
rename to samples/browseable/SkeletonWearableApp/res/values/dimens.xml
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/res/values/strings.xml b/samples/browseable/SkeletonWearableApp/res/values/strings.xml
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/res/values/strings.xml
rename to samples/browseable/SkeletonWearableApp/res/values/strings.xml
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/src/com.example.android.google.wearable.app/GridExampleActivity.java b/samples/browseable/SkeletonWearableApp/src/com.example.android.google.wearable.app/GridExampleActivity.java
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/src/com.example.android.google.wearable.app/GridExampleActivity.java
rename to samples/browseable/SkeletonWearableApp/src/com.example.android.google.wearable.app/GridExampleActivity.java
diff --git a/samples/browseable/SkeletonWearableApp/Wearable/src/com.example.android.google.wearable.app/MainActivity.java b/samples/browseable/SkeletonWearableApp/src/com.example.android.google.wearable.app/MainActivity.java
similarity index 100%
rename from samples/browseable/SkeletonWearableApp/Wearable/src/com.example.android.google.wearable.app/MainActivity.java
rename to samples/browseable/SkeletonWearableApp/src/com.example.android.google.wearable.app/MainActivity.java
diff --git a/samples/browseable/SpeedTracker/Application/res/layout/activity_main.xml b/samples/browseable/SpeedTracker/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/SpeedTracker/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/TextLinkify/res/layout/activity_main.xml b/samples/browseable/TextLinkify/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/TextLinkify/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/TextSwitcher/res/layout/activity_main.xml b/samples/browseable/TextSwitcher/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/TextSwitcher/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/Timer/Wearable/AndroidManifest.xml b/samples/browseable/Timer/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/Timer/Wearable/AndroidManifest.xml
rename to samples/browseable/Timer/AndroidManifest.xml
diff --git a/samples/browseable/Timer/Application/AndroidManifest.xml b/samples/browseable/Timer/Application/AndroidManifest.xml
deleted file mode 100644
index 2c60ab9..0000000
--- a/samples/browseable/Timer/Application/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2014 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.timer">
-
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="21" />
-
- <application android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
- </application>
-
-</manifest>
-
diff --git a/samples/browseable/Timer/Application/res/drawable-hdpi/ic_launcher.png b/samples/browseable/Timer/Application/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/Timer/Application/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Timer/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/Timer/Application/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/Timer/Application/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Timer/Application/res/drawable-mdpi/ic_launcher.png b/samples/browseable/Timer/Application/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/Timer/Application/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Timer/Application/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/Timer/Application/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/Timer/Application/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Timer/Application/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/Timer/Application/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/Timer/Application/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/Timer/Application/res/layout/activity_main.xml b/samples/browseable/Timer/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/Timer/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/Timer/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/Timer/Application/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2..0000000
--- a/samples/browseable/Timer/Application/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- 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/Timer/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/Timer/Application/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/Timer/Application/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/Timer/Application/res/values-v11/template-styles.xml b/samples/browseable/Timer/Application/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66..0000000
--- a/samples/browseable/Timer/Application/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- 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/Timer/Application/res/values-v21/base-colors.xml b/samples/browseable/Timer/Application/res/values-v21/base-colors.xml
deleted file mode 100644
index 34c9cd1..0000000
--- a/samples/browseable/Timer/Application/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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>
-
-
-</resources>
diff --git a/samples/browseable/Timer/Application/res/values-v21/base-template-styles.xml b/samples/browseable/Timer/Application/res/values-v21/base-template-styles.xml
deleted file mode 100644
index 0b2948f..0000000
--- a/samples/browseable/Timer/Application/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
- </style>
-
-</resources>
diff --git a/samples/browseable/Timer/Application/res/values/base-strings.xml b/samples/browseable/Timer/Application/res/values/base-strings.xml
deleted file mode 100644
index 0ccfd50..0000000
--- a/samples/browseable/Timer/Application/res/values/base-strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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">Timer</string>
- <string name="intro_message">
- <![CDATA[
-
-
- This simple wearable app allows the user to set a countdown timer. It runs
- independently on the wearable with no phone connection.
-
-
- ]]>
- </string>
-</resources>
diff --git a/samples/browseable/Timer/Application/res/values/template-dimens.xml b/samples/browseable/Timer/Application/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/Timer/Application/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/Timer/Application/res/values/template-styles.xml b/samples/browseable/Timer/Application/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593..0000000
--- a/samples/browseable/Timer/Application/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- 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/Timer/Wearable/res/drawable/ic_cc_alarm.png b/samples/browseable/Timer/res/drawable/ic_cc_alarm.png
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/drawable/ic_cc_alarm.png
rename to samples/browseable/Timer/res/drawable/ic_cc_alarm.png
Binary files differ
diff --git a/samples/browseable/Timer/Wearable/res/drawable/wl_circle.xml b/samples/browseable/Timer/res/drawable/wl_circle.xml
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/drawable/wl_circle.xml
rename to samples/browseable/Timer/res/drawable/wl_circle.xml
diff --git a/samples/browseable/Timer/Wearable/res/layout/timer_list_item.xml b/samples/browseable/Timer/res/layout/timer_list_item.xml
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/layout/timer_list_item.xml
rename to samples/browseable/Timer/res/layout/timer_list_item.xml
diff --git a/samples/browseable/Timer/Wearable/res/layout/timer_set_timer.xml b/samples/browseable/Timer/res/layout/timer_set_timer.xml
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/layout/timer_set_timer.xml
rename to samples/browseable/Timer/res/layout/timer_set_timer.xml
diff --git a/samples/browseable/Timer/Wearable/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/Timer/res/mipmap-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/mipmap-hdpi/ic_launcher.png
rename to samples/browseable/Timer/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Timer/Wearable/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/Timer/res/mipmap-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/mipmap-mdpi/ic_launcher.png
rename to samples/browseable/Timer/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Timer/Wearable/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/Timer/res/mipmap-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/mipmap-xhdpi/ic_launcher.png
rename to samples/browseable/Timer/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Timer/Wearable/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/Timer/res/mipmap-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/mipmap-xxhdpi/ic_launcher.png
rename to samples/browseable/Timer/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Timer/Wearable/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/Timer/res/mipmap-xxxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/mipmap-xxxhdpi/ic_launcher.png
rename to samples/browseable/Timer/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/Timer/Wearable/res/values/colors.xml b/samples/browseable/Timer/res/values/colors.xml
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/values/colors.xml
rename to samples/browseable/Timer/res/values/colors.xml
diff --git a/samples/browseable/Timer/Wearable/res/values/integers.xml b/samples/browseable/Timer/res/values/integers.xml
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/values/integers.xml
rename to samples/browseable/Timer/res/values/integers.xml
diff --git a/samples/browseable/Timer/Wearable/res/values/strings.xml b/samples/browseable/Timer/res/values/strings.xml
similarity index 100%
rename from samples/browseable/Timer/Wearable/res/values/strings.xml
rename to samples/browseable/Timer/res/values/strings.xml
diff --git a/samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/SetTimerActivity.java b/samples/browseable/Timer/src/com.example.android.wearable.timer/SetTimerActivity.java
similarity index 100%
rename from samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/SetTimerActivity.java
rename to samples/browseable/Timer/src/com.example.android.wearable.timer/SetTimerActivity.java
diff --git a/samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/TimerNotificationService.java b/samples/browseable/Timer/src/com.example.android.wearable.timer/TimerNotificationService.java
similarity index 100%
rename from samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/TimerNotificationService.java
rename to samples/browseable/Timer/src/com.example.android.wearable.timer/TimerNotificationService.java
diff --git a/samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/WearableListItemLayout.java b/samples/browseable/Timer/src/com.example.android.wearable.timer/WearableListItemLayout.java
similarity index 100%
rename from samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/WearableListItemLayout.java
rename to samples/browseable/Timer/src/com.example.android.wearable.timer/WearableListItemLayout.java
diff --git a/samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/util/Constants.java b/samples/browseable/Timer/src/com.example.android.wearable.timer/util/Constants.java
similarity index 100%
rename from samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/util/Constants.java
rename to samples/browseable/Timer/src/com.example.android.wearable.timer/util/Constants.java
diff --git a/samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/util/TimerFormat.java b/samples/browseable/Timer/src/com.example.android.wearable.timer/util/TimerFormat.java
similarity index 100%
rename from samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/util/TimerFormat.java
rename to samples/browseable/Timer/src/com.example.android.wearable.timer/util/TimerFormat.java
diff --git a/samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/util/TimerObj.java b/samples/browseable/Timer/src/com.example.android.wearable.timer/util/TimerObj.java
similarity index 100%
rename from samples/browseable/Timer/Wearable/src/com.example.android.wearable.timer/util/TimerObj.java
rename to samples/browseable/Timer/src/com.example.android.wearable.timer/util/TimerObj.java
diff --git a/samples/browseable/WatchFace/Application/res/layout/activity_main.xml b/samples/browseable/WatchFace/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/WatchFace/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/WatchFace/Wearable/AndroidManifest.xml b/samples/browseable/WatchFace/Wearable/AndroidManifest.xml
index ee906b7..14fee36 100644
--- a/samples/browseable/WatchFace/Wearable/AndroidManifest.xml
+++ b/samples/browseable/WatchFace/Wearable/AndroidManifest.xml
@@ -37,8 +37,6 @@
<service
android:name=".AnalogWatchFaceService"
android:label="@string/analog_name"
- android:allowEmbedded="true"
- android:taskAffinity=""
android:permission="android.permission.BIND_WALLPAPER" >
<meta-data
android:name="android.service.wallpaper"
@@ -47,7 +45,7 @@
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/preview_analog" />
<meta-data
- android:name="com.google.android.clockwork.home.preview_circular"
+ android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/preview_analog_circular" />
<meta-data
android:name="com.google.android.wearable.watchface.companionConfigurationAction"
@@ -61,8 +59,6 @@
<service
android:name=".SweepWatchFaceService"
android:label="@string/sweep_name"
- android:allowEmbedded="true"
- android:taskAffinity=""
android:permission="android.permission.BIND_WALLPAPER" >
<meta-data
android:name="android.service.wallpaper"
@@ -71,7 +67,7 @@
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/preview_analog" />
<meta-data
- android:name="com.google.android.clockwork.home.preview_circular"
+ android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/preview_analog_circular" />
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
@@ -82,8 +78,6 @@
<service
android:name=".TiltWatchFaceService"
android:label="@string/tilt_name"
- android:allowEmbedded="true"
- android:taskAffinity=""
android:permission="android.permission.BIND_WALLPAPER" >
<meta-data
android:name="android.service.wallpaper"
@@ -92,7 +86,7 @@
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/preview_tilt" />
<meta-data
- android:name="com.google.android.clockwork.home.preview_circular"
+ android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/preview_tilt_circular" />
<meta-data
android:name="com.google.android.wearable.watchface.companionConfigurationAction"
@@ -107,8 +101,6 @@
<service
android:name=".CardBoundsWatchFaceService"
android:label="@string/card_bounds_name"
- android:allowEmbedded="true"
- android:taskAffinity=""
android:permission="android.permission.BIND_WALLPAPER" >
<meta-data
android:name="android.service.wallpaper"
@@ -117,7 +109,7 @@
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/preview_card_bounds" />
<meta-data
- android:name="com.google.android.clockwork.home.preview_circular"
+ android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/preview_card_bounds_circular" />
<meta-data
android:name="com.google.android.wearable.watchface.companionConfigurationAction"
@@ -131,8 +123,6 @@
<service
android:name=".DigitalWatchFaceService"
android:label="@string/digital_name"
- android:allowEmbedded="true"
- android:taskAffinity=""
android:permission="android.permission.BIND_WALLPAPER" >
<meta-data
android:name="android.service.wallpaper"
@@ -141,7 +131,7 @@
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/preview_digital" />
<meta-data
- android:name="com.google.android.clockwork.home.preview_circular"
+ android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/preview_digital_circular" />
<meta-data
android:name="com.google.android.wearable.watchface.companionConfigurationAction"
@@ -172,8 +162,6 @@
<service
android:name=".CalendarWatchFaceService"
android:label="@string/calendar_name"
- android:allowEmbedded="true"
- android:taskAffinity=""
android:permission="android.permission.BIND_WALLPAPER" >
<meta-data
android:name="android.service.wallpaper"
@@ -182,7 +170,7 @@
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/preview_calendar" />
<meta-data
- android:name="com.google.android.clockwork.home.preview_circular"
+ android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/preview_calendar_circular" />
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
diff --git a/samples/browseable/WatchViewStub/Wearable/AndroidManifest.xml b/samples/browseable/WatchViewStub/AndroidManifest.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/AndroidManifest.xml
rename to samples/browseable/WatchViewStub/AndroidManifest.xml
diff --git a/samples/browseable/WatchViewStub/Application/AndroidManifest.xml b/samples/browseable/WatchViewStub/Application/AndroidManifest.xml
deleted file mode 100644
index b1b7103..0000000
--- a/samples/browseable/WatchViewStub/Application/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2014 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.google.wearable.watchviewstub">
-
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="21" />
-
- <application android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name">
- </application>
-
-</manifest>
-
diff --git a/samples/browseable/WatchViewStub/Application/res/drawable-hdpi/ic_launcher.png b/samples/browseable/WatchViewStub/Application/res/drawable-hdpi/ic_launcher.png
deleted file mode 100755
index 589f229..0000000
--- a/samples/browseable/WatchViewStub/Application/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/WatchViewStub/Application/res/drawable-hdpi/tile.9.png
deleted file mode 100644
index 1358628..0000000
--- a/samples/browseable/WatchViewStub/Application/res/drawable-hdpi/tile.9.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Application/res/drawable-mdpi/ic_launcher.png b/samples/browseable/WatchViewStub/Application/res/drawable-mdpi/ic_launcher.png
deleted file mode 100755
index 77dd571..0000000
--- a/samples/browseable/WatchViewStub/Application/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Application/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/WatchViewStub/Application/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100755
index fe34ebe..0000000
--- a/samples/browseable/WatchViewStub/Application/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Application/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/WatchViewStub/Application/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100755
index ab80bcd..0000000
--- a/samples/browseable/WatchViewStub/Application/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Application/res/layout/activity_main.xml b/samples/browseable/WatchViewStub/Application/res/layout/activity_main.xml
deleted file mode 100755
index be1aa49..0000000
--- a/samples/browseable/WatchViewStub/Application/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
- 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:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout style="@style/Widget.SampleMessageTile"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/Widget.SampleMessage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/horizontal_page_margin"
- android:layout_marginRight="@dimen/horizontal_page_margin"
- android:layout_marginTop="@dimen/vertical_page_margin"
- android:layout_marginBottom="@dimen/vertical_page_margin"
- android:text="@string/intro_message" />
- </LinearLayout>
-</LinearLayout>
diff --git a/samples/browseable/WatchViewStub/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/WatchViewStub/Application/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2..0000000
--- a/samples/browseable/WatchViewStub/Application/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- 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/WatchViewStub/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/WatchViewStub/Application/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d1974..0000000
--- a/samples/browseable/WatchViewStub/Application/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- 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/WatchViewStub/Application/res/values-v11/template-styles.xml b/samples/browseable/WatchViewStub/Application/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66..0000000
--- a/samples/browseable/WatchViewStub/Application/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- 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/WatchViewStub/Application/res/values-v21/base-colors.xml b/samples/browseable/WatchViewStub/Application/res/values-v21/base-colors.xml
deleted file mode 100644
index 34c9cd1..0000000
--- a/samples/browseable/WatchViewStub/Application/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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>
-
-
-</resources>
diff --git a/samples/browseable/WatchViewStub/Application/res/values-v21/base-template-styles.xml b/samples/browseable/WatchViewStub/Application/res/values-v21/base-template-styles.xml
deleted file mode 100644
index 0b2948f..0000000
--- a/samples/browseable/WatchViewStub/Application/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
- </style>
-
-</resources>
diff --git a/samples/browseable/WatchViewStub/Application/res/values/template-dimens.xml b/samples/browseable/WatchViewStub/Application/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b..0000000
--- a/samples/browseable/WatchViewStub/Application/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- 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/WatchViewStub/Application/res/values/template-styles.xml b/samples/browseable/WatchViewStub/Application/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593..0000000
--- a/samples/browseable/WatchViewStub/Application/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- 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/WatchViewStub/Wearable/res/drawable-hdpi/ic_launcher.png b/samples/browseable/WatchViewStub/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/drawable-hdpi/ic_launcher.png
rename to samples/browseable/WatchViewStub/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Wearable/res/drawable-mdpi/ic_launcher.png b/samples/browseable/WatchViewStub/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/drawable-mdpi/ic_launcher.png
rename to samples/browseable/WatchViewStub/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Wearable/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/WatchViewStub/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/drawable-xhdpi/ic_launcher.png
rename to samples/browseable/WatchViewStub/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Wearable/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/WatchViewStub/res/drawable-xxhdpi/ic_launcher.png
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/drawable-xxhdpi/ic_launcher.png
rename to samples/browseable/WatchViewStub/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/WatchViewStub/Wearable/res/drawable/rect_background.xml b/samples/browseable/WatchViewStub/res/drawable/rect_background.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/drawable/rect_background.xml
rename to samples/browseable/WatchViewStub/res/drawable/rect_background.xml
diff --git a/samples/browseable/WatchViewStub/Wearable/res/drawable/round_background.xml b/samples/browseable/WatchViewStub/res/drawable/round_background.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/drawable/round_background.xml
rename to samples/browseable/WatchViewStub/res/drawable/round_background.xml
diff --git a/samples/browseable/WatchViewStub/Wearable/res/layout/main_activity.xml b/samples/browseable/WatchViewStub/res/layout/main_activity.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/layout/main_activity.xml
rename to samples/browseable/WatchViewStub/res/layout/main_activity.xml
diff --git a/samples/browseable/WatchViewStub/Wearable/res/layout/rect_layout.xml b/samples/browseable/WatchViewStub/res/layout/rect_layout.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/layout/rect_layout.xml
rename to samples/browseable/WatchViewStub/res/layout/rect_layout.xml
diff --git a/samples/browseable/WatchViewStub/Wearable/res/layout/round_layout.xml b/samples/browseable/WatchViewStub/res/layout/round_layout.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/layout/round_layout.xml
rename to samples/browseable/WatchViewStub/res/layout/round_layout.xml
diff --git a/samples/browseable/WatchViewStub/Wearable/res/values/dimens.xml b/samples/browseable/WatchViewStub/res/values/dimens.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/values/dimens.xml
rename to samples/browseable/WatchViewStub/res/values/dimens.xml
diff --git a/samples/browseable/WatchViewStub/Wearable/res/values/strings.xml b/samples/browseable/WatchViewStub/res/values/strings.xml
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/res/values/strings.xml
rename to samples/browseable/WatchViewStub/res/values/strings.xml
diff --git a/samples/browseable/WatchViewStub/Wearable/src/com.example.android.google.wearable.watchviewstub/MainActivity.java b/samples/browseable/WatchViewStub/src/com.example.android.google.wearable.watchviewstub/MainActivity.java
similarity index 100%
rename from samples/browseable/WatchViewStub/Wearable/src/com.example.android.google.wearable.watchviewstub/MainActivity.java
rename to samples/browseable/WatchViewStub/src/com.example.android.google.wearable.watchviewstub/MainActivity.java
diff --git a/samples/browseable/XYZTouristAttractions/Application/AndroidManifest.xml b/samples/browseable/XYZTouristAttractions/Application/AndroidManifest.xml
new file mode 100644
index 0000000..5f330d5
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/AndroidManifest.xml
@@ -0,0 +1,63 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.xyztouristattractions" >
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/XYZAppTheme" >
+
+ <activity
+ android:name=".ui.AttractionListActivity"
+ android:label="@string/app_name" >
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+
+ </activity>
+
+ <activity
+ android:name=".ui.DetailActivity"
+ android:label="@string/app_name"
+ android:theme="@style/XYZAppTheme.Detail"
+ android:parentActivityName=".ui.AttractionListActivity" />
+
+ <receiver android:name=".service.UtilityReceiver" />
+
+ <service android:name=".service.UtilityService" />
+
+ <service android:name=".service.ListenerService">
+ <intent-filter>
+ <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
+ </intent-filter>
+ </service>
+
+ <meta-data android:name="com.google.android.gms.version"
+ android:value="@integer/google_play_services_version" />
+
+ </application>
+
+</manifest>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/ic_action_map.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/ic_action_map.png
new file mode 100755
index 0000000..b478e43
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/ic_action_map.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/ic_stat_maps_pin_drop.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/ic_stat_maps_pin_drop.png
new file mode 100755
index 0000000..78821eb
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/ic_stat_maps_pin_drop.png
Binary files differ
diff --git a/samples/browseable/ElizaChat/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/tile.9.png
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/drawable-hdpi/tile.9.png
copy to samples/browseable/XYZTouristAttractions/Application/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-mdpi/ic_action_map.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-mdpi/ic_action_map.png
new file mode 100755
index 0000000..0bfa259
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-mdpi/ic_action_map.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-mdpi/ic_stat_maps_pin_drop.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-mdpi/ic_stat_maps_pin_drop.png
new file mode 100755
index 0000000..f7f0dce
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-mdpi/ic_stat_maps_pin_drop.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-nodpi/empty_photo.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-nodpi/empty_photo.png
new file mode 100644
index 0000000..da1478a
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-nodpi/empty_photo.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-xhdpi/ic_action_map.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xhdpi/ic_action_map.png
new file mode 100755
index 0000000..23468bb
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xhdpi/ic_action_map.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-xhdpi/ic_stat_maps_pin_drop.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xhdpi/ic_stat_maps_pin_drop.png
new file mode 100755
index 0000000..6abaad0
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xhdpi/ic_stat_maps_pin_drop.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-xxhdpi/ic_action_map.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xxhdpi/ic_action_map.png
new file mode 100755
index 0000000..9ce29d1
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xxhdpi/ic_action_map.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/drawable-xxhdpi/ic_stat_maps_pin_drop.png b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xxhdpi/ic_stat_maps_pin_drop.png
new file mode 100755
index 0000000..1e523e6
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/drawable-xxhdpi/ic_stat_maps_pin_drop.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/layout/activity_main.xml b/samples/browseable/XYZTouristAttractions/Application/res/layout/activity_main.xml
new file mode 100644
index 0000000..8661610
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/layout/activity_main.xml
@@ -0,0 +1,25 @@
+<!--
+ 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".MainActivity">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/layout/fragment_detail.xml b/samples/browseable/XYZTouristAttractions/Application/res/layout/fragment_detail.xml
new file mode 100644
index 0000000..dffeb4e
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/layout/fragment_detail.xml
@@ -0,0 +1,82 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:weightSum="100">
+
+ <ImageView
+ android:id="@+id/imageView"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="50"
+ android:scaleType="centerCrop"
+ android:transitionName="image" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?colorPrimary"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/keyline2"
+ android:paddingEnd="@dimen/keyline3"
+ android:paddingTop="@dimen/standard_margin"
+ android:paddingBottom="@dimen/standard_margin"
+ android:elevation="2dp">
+
+ <TextView
+ android:id="@+id/nameTextView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:textIsSelectable="true"
+ style="@style/TextAppearance.AppCompat.Title.Inverse"
+ android:transitionName="title" />
+
+ <TextView
+ android:id="@+id/distanceTextView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.AppCompat.Subhead.Inverse" />
+
+ </LinearLayout>
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="50"
+ android:paddingTop="@dimen/standard_margin"
+ android:paddingBottom="@dimen/standard_margin"
+ android:scrollbarStyle="outsideOverlay"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@+id/descriptionTextView"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:textIsSelectable="true"
+ style="@style/TextAppearance.AppCompat.Body1"
+ android:paddingStart="@dimen/keyline2"
+ android:paddingEnd="@dimen/keyline3" />
+
+ </ScrollView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/layout/fragment_main.xml b/samples/browseable/XYZTouristAttractions/Application/res/layout/fragment_main.xml
new file mode 100644
index 0000000..94d110a
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/layout/fragment_main.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".AttractionListFragment">
+
+ <com.example.android.xyztouristattractions.ui.AttractionsRecyclerView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scrollbars="vertical" />
+
+ <TextView
+ android:id="@android:id/empty"
+ android:text="@string/empty_list"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center" />
+
+</LinearLayout>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/layout/list_row.xml b/samples/browseable/XYZTouristAttractions/Application/res/layout/list_row.xml
new file mode 100644
index 0000000..25f55d0
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/layout/list_row.xml
@@ -0,0 +1,76 @@
+<?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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:foreground="?attr/selectableItemBackground"
+ android:layout_height="@dimen/image_size">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/image_size"
+ android:layout_height="match_parent"
+ android:src="@drawable/empty_photo"
+ android:scaleType="centerCrop"
+ android:transitionName="image" />
+
+ <TextView
+ android:id="@+id/overlaytext"
+ android:layout_width="@dimen/image_size"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@android:id/icon"
+ android:gravity="center"
+ android:padding="@dimen/tiny_margin"
+ style="?android:textAppearanceSmallInverse"
+ android:background="@color/text_background"
+ tools:text="Overlay"/>
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@android:id/icon"
+ android:paddingTop="@dimen/small_margin"
+ android:paddingLeft="@dimen/small_margin"
+ android:paddingRight="@dimen/small_margin"
+ android:maxLines="1"
+ android:ellipsize="end"
+ style="?android:textAppearanceMedium"
+ tools:text="Title 1"
+ android:transitionName="image" />
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@android:id/icon"
+ android:layout_below="@android:id/text1"
+ android:padding="@dimen/small_margin"
+ android:ellipsize="end"
+ android:maxLines="4"
+ style="?android:textAppearanceSmall"
+ tools:text="Description goes here" />
+
+ </RelativeLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/menu/detail.xml b/samples/browseable/XYZTouristAttractions/Application/res/menu/detail.xml
new file mode 100644
index 0000000..aeb5c98
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/menu/detail.xml
@@ -0,0 +1,28 @@
+<!--
+ 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.
+ -->
+
+<menu 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"
+ tools:context=".MainActivity" >
+
+ <item android:id="@+id/map"
+ android:title="@string/action_map"
+ android:orderInCategory="100"
+ android:icon="@drawable/ic_action_map"
+ app:showAsAction="ifRoom" />
+
+</menu>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/menu/main.xml b/samples/browseable/XYZTouristAttractions/Application/res/menu/main.xml
new file mode 100644
index 0000000..1f840a7
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/menu/main.xml
@@ -0,0 +1,37 @@
+<!--
+ 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.
+ -->
+
+<menu 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"
+ tools:context=".MainActivity" >
+
+ <item android:id="@+id/test_notification"
+ android:title="@string/action_test_notification"
+ android:orderInCategory="100"
+ app:showAsAction="never" />
+
+ <item android:id="@+id/test_microapp"
+ android:title="@string/action_test_microapp"
+ android:orderInCategory="100"
+ app:showAsAction="never" />
+
+ <item android:id="@+id/test_toggle_geofence"
+ android:title="@string/action_test_toggle_geofence"
+ android:orderInCategory="100"
+ app:showAsAction="never" />
+
+</menu>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..d159cbe
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..cae9b67
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..cc42dd2
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..a84fd3a
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..f596c5c
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/transition-v21/explode.xml b/samples/browseable/XYZTouristAttractions/Application/res/transition-v21/explode.xml
new file mode 100644
index 0000000..5dfa717
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/transition-v21/explode.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <explode>
+ <targets>
+ <target android:targetClass="android.widget.TextView" />
+ <target android:targetClass="android.widget.FrameLayout" />
+ <target android:targetClass="android.widget.LinearLayout" />
+ <target android:targetClass="android.widget.ImageView" />
+ </targets>
+ </explode>
+
+</transitionSet>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/transition-v21/fade.xml b/samples/browseable/XYZTouristAttractions/Application/res/transition-v21/fade.xml
new file mode 100644
index 0000000..6c70ec5
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/transition-v21/fade.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <fade>
+ <targets>
+ <target android:targetClass="android.widget.TextView" />
+ <target android:targetClass="android.widget.FrameLayout" />
+ <target android:targetClass="android.widget.LinearLayout" />
+ <target android:targetClass="android.widget.ImageView" />
+ </targets>
+ </fade>
+
+</transitionSet>
\ No newline at end of file
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-sw400dp/dimens.xml
similarity index 65%
copy from samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-sw400dp/dimens.xml
index 22074a2..ad90eaa 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values-sw400dp/dimens.xml
@@ -1,11 +1,11 @@
<!--
- Copyright 2013 The Android Open Source Project
+ 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
+ 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,
@@ -16,9 +16,9 @@
<resources>
- <!-- Semantic definitions -->
-
- <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
- <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="standard_margin">24dp</dimen>
+ <dimen name="small_margin">12dp</dimen>
+ <dimen name="tiny_margin">8dp</dimen>
+ <dimen name="image_size">140dp</dimen>
</resources>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-sw600dp/dimens.xml
similarity index 62%
copy from samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-sw600dp/dimens.xml
index 22074a2..4e3a48e 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values-sw600dp/dimens.xml
@@ -1,11 +1,11 @@
<!--
- Copyright 2013 The Android Open Source Project
+ 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
+ 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,
@@ -16,9 +16,10 @@
<resources>
- <!-- Semantic definitions -->
-
- <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
- <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="standard_margin">24dp</dimen>
+ <dimen name="small_margin">12dp</dimen>
+ <dimen name="tiny_margin">8dp</dimen>
+ <dimen name="image_size">200dp</dimen>
+ <dimen name="keyline2">80dp</dimen>
</resources>
diff --git a/samples/browseable/ElizaChat/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-sw600dp/template-dimens.xml
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-sw600dp/template-dimens.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-sw600dp/template-styles.xml
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/values-sw600dp/template-styles.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-sw600dp/template-styles.xml
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-sw720dp/dimens.xml
similarity index 65%
rename from samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
rename to samples/browseable/XYZTouristAttractions/Application/res/values-sw720dp/dimens.xml
index 22074a2..73f0b8d 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values-sw720dp/dimens.xml
@@ -1,11 +1,11 @@
<!--
- Copyright 2013 The Android Open Source Project
+ 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
+ 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,
@@ -16,9 +16,9 @@
<resources>
- <!-- Semantic definitions -->
-
- <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
- <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="standard_margin">24dp</dimen>
+ <dimen name="small_margin">12dp</dimen>
+ <dimen name="tiny_margin">12dp</dimen>
+ <dimen name="image_size">250dp</dimen>
</resources>
diff --git a/samples/browseable/ElizaChat/Application/res/values-v11/template-styles.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-v11/template-styles.xml
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/values-v11/template-styles.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-v11/template-styles.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values-v21/base-colors.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-v21/base-colors.xml
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/values-v21/base-colors.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-v21/base-colors.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values-v21/base-template-styles.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-v21/base-template-styles.xml
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/values-v21/base-template-styles.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-v21/base-template-styles.xml
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values-v21/styles.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-v21/styles.xml
new file mode 100644
index 0000000..319d664
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values-v21/styles.xml
@@ -0,0 +1,31 @@
+<!--
+ 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.
+ -->
+
+<resources>
+
+ <style name="XYZAppTheme" parent="XYZAppTheme.Base">
+ <item name="android:windowContentTransitions">true</item>
+ <item name="android:windowActivityTransitions">true</item>
+ <item name="android:windowExitTransition">@transition/fade</item>
+ <item name="android:windowReenterTransition">@transition/fade</item>
+ <item name="android:windowEnterTransition">@transition/explode</item>
+ <item name="android:windowReturnTransition">@transition/explode</item>
+ <item name="android:windowAllowEnterTransitionOverlap">false</item>
+ <item name="android:windowAllowReturnTransitionOverlap">false</item>
+ <item name="android:windowSharedElementsUseOverlay">false</item>
+ </style>
+
+</resources>
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml b/samples/browseable/XYZTouristAttractions/Application/res/values-w820dp/dimens.xml
similarity index 72%
copy from samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values-w820dp/dimens.xml
index 8c1ea66..069102e 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values-w820dp/dimens.xml
@@ -1,11 +1,11 @@
<!--
- Copyright 2013 The Android Open Source Project
+ 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
+ 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,
@@ -16,7 +16,6 @@
<resources>
- <!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+ <integer name="list_columns">2</integer>
</resources>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values/base-strings.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/base-strings.xml
new file mode 100644
index 0000000..e551fe1
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values/base-strings.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.
+-->
+<resources>
+ <string name="app_name">XYZTouristAttractions</string>
+ <string name="intro_message">
+ <![CDATA[
+
+
+This sample aims to be as close to a real world example of a mobile
+and Wear app combination as possible. It has a more refined design
+and also provides a practical example of how a mobile app would
+interact and communicate with its wear counterpart.
+
+The app itself is modeled after a hypothetical tourist attractions
+app that notifies the user when they are in close proximity to
+notable points of interest.
+
+The Wear component loads a full wearable app that shows images,
+summary information and provides quick actions for nearby tourist
+attractions in a GridViewPager UI component.
+
+
+ ]]>
+ </string>
+</resources>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values/colors.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/colors.xml
new file mode 100644
index 0000000..142f548
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values/colors.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<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>
+
+</resources>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values/dimens.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/dimens.xml
new file mode 100644
index 0000000..03acfef
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values/dimens.xml
@@ -0,0 +1,29 @@
+<!--
+ 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.
+ -->
+
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="standard_margin">16dp</dimen>
+ <dimen name="small_margin">8dp</dimen>
+ <dimen name="tiny_margin">4dp</dimen>
+ <dimen name="image_size">120dp</dimen>
+ <item type="dimen" name="keyline1">@dimen/standard_margin</item>
+ <dimen name="keyline2">72dp</dimen>
+ <item type="dimen" name="keyline3">@dimen/standard_margin</item>
+ <integer name="list_columns">1</integer>
+
+</resources>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values/strings.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/strings.xml
new file mode 100644
index 0000000..5f3ee14
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values/strings.xml
@@ -0,0 +1,49 @@
+<?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.
+ -->
+
+<resources>
+
+ <string name="empty_list">No attractions found nearby!</string>
+ <string name="nearby_attraction">Nearby tourist attraction</string>
+ <string name="action_test_notification">Test Notification</string>
+ <string name="action_test_notification_dialog">
+<![CDATA[
+This will trigger a rich notification that is bridged over to the Wear device.
+\n\nNote that the first time this runs it can take 20-30 seconds to transfer
+the image data (in practice, users wouldn\'t notice this as the notification
+would be triggered automatically and only show up after the data was
+transferred).
+]]>
+ </string>
+ <string name="action_test_microapp">Test Micro App</string>
+ <string name="action_test_microapp_dialog">
+<![CDATA[
+This will trigger a notification on Wear that lets you open up the full
+wearable app. A few things to note:\n\n
+• Ensure you have the wearable APK installed on your Wear device (debug builds
+require you to install it manually)\n\n
+• The first time this runs it can take 20-30 seconds to transfer the image
+data (in practice, users wouldn\'t notice this as the notification would only
+be triggered after the data was transferred)\n\n
+• The notification priority is set to the recommended default value so it may
+appear further down your stream
+]]>
+ </string>
+ <string name="action_test_toggle_geofence">Toggle Geofence Trigger</string>
+ <string name="action_map">Show on Map</string>
+
+</resources>
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values/styles.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/styles.xml
new file mode 100644
index 0000000..05f1e47
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values/styles.xml
@@ -0,0 +1,39 @@
+<!--
+ 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.
+ -->
+
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="XYZAppTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ <item name="colorControlHighlight">?colorAccent</item>
+ </style>
+
+ <style name="XYZAppTheme" parent="XYZAppTheme.Base" />
+
+ <style name="XYZAppTheme.Detail">
+ <item name="windowActionBarOverlay">true</item>
+ <item name="actionBarStyle">@style/ActionBar.Detail</item>
+ </style>
+
+ <style name="ActionBar.Detail" parent="Widget.AppCompat.Light.ActionBar.Solid.Inverse">
+ <item name="background">@color/transparent_actionbar_background</item>
+ <item name="displayOptions">showHome|homeAsUp</item>
+ </style>
+
+</resources>
diff --git a/samples/browseable/ElizaChat/Application/res/values/template-dimens.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/template-dimens.xml
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/values/template-dimens.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values/template-dimens.xml
diff --git a/samples/browseable/ElizaChat/Application/res/values/template-styles.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/template-styles.xml
similarity index 100%
copy from samples/browseable/ElizaChat/Application/res/values/template-styles.xml
copy to samples/browseable/XYZTouristAttractions/Application/res/values/template-styles.xml
diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/provider/TouristAttractions.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/provider/TouristAttractions.java
new file mode 100644
index 0000000..50be362
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/provider/TouristAttractions.java
@@ -0,0 +1,147 @@
+/*
+ * 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.provider;
+
+import android.net.Uri;
+
+import com.example.android.xyztouristattractions.BuildConfig;
+import com.example.android.xyztouristattractions.common.Attraction;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.maps.android.SphericalUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Static data content provider.
+ */
+public class TouristAttractions {
+
+ public static final String CITY_SYDNEY = "Sydney";
+
+ public static final String TEST_CITY = CITY_SYDNEY;
+
+ private static final float TRIGGER_RADIUS = 2000; // 2KM
+ private static final int TRIGGER_TRANSITION = Geofence.GEOFENCE_TRANSITION_ENTER |
+ Geofence.GEOFENCE_TRANSITION_EXIT;
+ private static final long EXPIRATION_DURATION = Geofence.NEVER_EXPIRE;
+
+ public static final Map<String, LatLng> CITY_LOCATIONS = new HashMap<String, LatLng>() {{
+ put(CITY_SYDNEY, new LatLng(-33.873651, 151.2068896));
+ }};
+
+ /**
+ * All photos used with permission under the Creative Commons Attribution-ShareAlike License.
+ */
+ public static final HashMap<String, List<Attraction>> ATTRACTIONS =
+ new HashMap<String, List<Attraction>>() {{
+
+ put(CITY_SYDNEY, new ArrayList<Attraction>() {{
+ add(new Attraction(
+ "Sydney Opera House",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae bibendum justo, vitae cursus velit. Suspendisse potenti.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vitae bibendum justo, vitae cursus velit. Suspendisse potenti. Suspendisse scelerisque risus justo, non tincidunt nibh blandit et. Vivamus elit lacus, luctus nec erat in, pharetra semper turpis. Quisque viverra nulla ligula, non pulvinar ante dictum sit amet. Vestibulum aliquet tortor mauris, vel suscipit nisl malesuada eget. Aliquam maximus dictum euismod. Maecenas leo quam, volutpat id diam eget, placerat fringilla ipsum. Nam pretium vehicula augue quis euismod.\n\nNam sed blandit magna. Vestibulum a fermentum arcu. Vestibulum et ligula at nisi luctus facilisis. Proin fermentum enim a nibh commodo finibus. Suspendisse justo elit, vulputate ut ipsum at, pellentesque auctor massa. Praesent vestibulum erat interdum imperdiet dapibus. In hac habitasse platea dictumst. Proin varius orci vitae tempor vulputate.\n\nEtiam sed mollis orci. Integer et ex sed tortor scelerisque blandit semper id libero. Nulla facilisi. Pellentesque tempor magna eget massa ultrices, et efficitur lectus finibus.",
+ Uri.parse("https://lh5.googleusercontent.com/-7fb5ybQhUbo/VGLWjIL4RmI/AAAAAAAAACM/2jLe_msj_tk/w600-no/IMG_0049.JPG"),
+ Uri.parse("https://lh3.googleusercontent.com/-EFEw6s7mT6I/VGLkCH4Xt4I/AAAAAAAAADY/ZlznhaQvb8E/w600-no/DSC_2775.JPG"),
+ new LatLng(-33.858667, 151.214028),
+ CITY_SYDNEY
+ ));
+
+ add(new Attraction(
+ "Sydney Harbour Bridge",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut nulla neque. Morbi nec felis vel neque rhoncus malesuada.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ut nulla neque. Morbi nec felis vel neque rhoncus malesuada. Mauris non nisi est. Nunc in ipsum euismod, suscipit dolor eget, efficitur nisi. Integer venenatis mauris mauris, quis luctus risus pellentesque a. Duis tempus est at ligula vehicula fermentum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.\n\nNam ut sodales nibh, euismod aliquet lectus. Curabitur ornare dictum nisi, at faucibus magna. Morbi tempus nibh sed sodales volutpat. Etiam sodales, turpis sit amet porttitor tristique, libero libero faucibus est, viverra dictum risus ipsum vel augue. Nulla dolor magna, iaculis ac ornare id, fermentum eget massa. Sed mattis, odio nec sodales vehicula, neque metus ullamcorper nulla, sit amet ullamcorper risus lectus a ipsum. Curabitur venenatis feugiat quam nec elementum. Curabitur a interdum urna. Curabitur tincidunt tortor eget neque condimentum blandit. Etiam imperdiet, enim nec blandit convallis, nunc augue.",
+ Uri.parse("https://lh6.googleusercontent.com/-ORRJtfLQlaw/VGLmQPv3n8I/AAAAAAAAAD8/2TzSCCPzl9k/w600-no/DSC04114.JPG"),
+ Uri.parse("https://lh4.googleusercontent.com/-ch9Kk-7pD68/VGLkCNh5niI/AAAAAAAAADc/ztxkRHWX-po/w600-no/DSC_2739.JPG"),
+ new LatLng(-33.852222, 151.210556),
+ CITY_SYDNEY
+ ));
+
+ add(new Attraction(
+ "Darling Harbour",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ut dui in ipsum suscipit aliquet pretium aliquet odio.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ut dui in ipsum suscipit aliquet pretium aliquet odio. Nam posuere nunc sed risus molestie varius. Suspendisse posuere faucibus urna, id vestibulum ante iaculis et. Vivamus placerat suscipit sem, a tempor nunc vehicula ac. Ut libero velit, dapibus sit amet euismod vel, dignissim a nisl.\n\nDonec non dui non felis laoreet malesuada. Fusce ac metus ultrices, fermentum felis quis, varius velit. Donec ac felis semper, scelerisque diam sed, dignissim risus. Maecenas vel semper sapien. Fusce euismod justo posuere, efficitur risus tincidunt, congue tellus. In hac habitasse platea dictumst. Sed lobortis risus consequat vehicula facilisis.\n\nIn hendrerit, neque in gravida rutrum, purus enim aliquet lectus, sit amet vulputate tortor lacus at sem. Aenean lorem metus, finibus rhoncus eros at, ullamcorper fringilla velit. Nulla vitae porttitor metus, quis gravida lectus. In rhoncus, diam a elementum luctus, erat nisi tempus ex, in porta est.",
+ Uri.parse("https://lh5.googleusercontent.com/-qX43g6s92LY/VGLaTT3N35I/AAAAAAAAAC8/BbueQmch0Rw/w600-no/68001.jpg"),
+ Uri.parse("https://lh6.googleusercontent.com/-SQ6T1Ure6l8/VGLaTg2iGuI/AAAAAAAAACo/m6_RkTW2G1o/w600-no/IMG_20140201_082851.jpg"),
+ new LatLng(-33.8723, 151.19896),
+ CITY_SYDNEY
+ ));
+
+ add(new Attraction(
+ "Bondi Beach",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam et nunc in leo laoreet placerat. Interdum et malesuada fames ac ante ipsum primis in faucibus.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam et nunc in leo laoreet placerat. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin vestibulum laoreet odio nec posuere. Quisque ante arcu, malesuada vitae velit a, auctor tincidunt ante. Mauris varius eros eros, eget scelerisque mi scelerisque ut. Donec vehicula vitae urna ac hendrerit. Phasellus egestas risus nec euismod auctor.\n\nInteger fermentum velit et dolor varius sagittis. Proin et viverra sapien. Nulla aliquet ante et hendrerit egestas. Duis vulputate libero in nisi gravida cursus. Praesent laoreet nec dolor non iaculis. Aliquam eleifend ultricies ipsum, eu pellentesque libero rutrum non. Mauris et purus erat. Nullam semper mi id tincidunt viverra. Ut porta sem congue lectus luctus ultricies. Suspendisse iaculis lacinia nibh, eu accumsan magna volutpat vel. Sed id interdum mi, vel sollicitudin elit. Fusce facilisis elementum gravida. Duis at volutpat odio. Integer porta convallis tincidunt. Donec aliquam, leo ut.",
+ Uri.parse("https://lh4.googleusercontent.com/-wbNgVdUkBiE/VHe99hGVtNI/AAAAAAAAAFY/fAHfhchNLJw/w600-no/IMG_20141124_143747.jpg"),
+ Uri.parse("https://lh6.googleusercontent.com/-sjY_xlEOic4/VHe9-I4DD9I/AAAAAAAAAFI/Mt0VnjU7SxQ/w600-no/IMG_20141124_144008.jpg"),
+ new LatLng(-33.89102, 151.277726),
+ CITY_SYDNEY
+ ));
+
+ add(new Attraction(
+ "Taronga Zoo",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam eros velit, faucibus in mi in, accumsan eleifend magna. Fusce efficitur volutpat leo nec finibus. Vivamus luctus quis dolor ac interdum.",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam eros velit, faucibus in mi in, accumsan eleifend magna. Fusce efficitur volutpat leo nec finibus. Vivamus luctus quis dolor ac interdum. Donec iaculis, orci quis semper vulputate, tortor nisi porttitor tortor, at pretium ante quam ut odio. Donec fringilla sapien et dolor pharetra ultrices. Aenean faucibus felis non vulputate iaculis.\n\nEtiam eget dapibus ligula. Nunc facilisis dignissim tortor et elementum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam condimentum pellentesque mollis. Aliquam finibus urna ipsum, sed accumsan ante blandit quis. Vestibulum vel lacinia ligula. Nunc justo ex, volutpat nec justo ut, efficitur gravida lectus. Mauris cursus dui libero, vel tristique purus laoreet non.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse ultrices ullamcorper est, at consequat massa. Nam egestas at urna at pellentesque. Quisque lacus quam, efficitur vel erat eget, placerat feugiat eros. Mauris.",
+ Uri.parse("https://lh6.googleusercontent.com/-kypwDfnk674/VGLWpQPm4VI/AAAAAAAAAB0/SrfL0fE9DnE/w500-no/OI000020_2.jpg"),
+ Uri.parse("https://lh3.googleusercontent.com/-6_Ioko2ysgU/VHva2PjmRCI/AAAAAAAAAGM/cHjJC7ney4Q/w500-no/PC190054.JPG"),
+ new LatLng(-33.843333, 151.241111),
+ CITY_SYDNEY
+ ));
+ }});
+
+ }};
+
+ /**
+ * Creates a list of geofences based on the city locations
+ */
+ public static List<Geofence> getGeofenceList() {
+ List<Geofence> geofenceList = new ArrayList<Geofence>();
+ for (String city : CITY_LOCATIONS.keySet()) {
+ LatLng cityLatLng = CITY_LOCATIONS.get(city);
+ geofenceList.add(new Geofence.Builder()
+ .setCircularRegion(cityLatLng.latitude, cityLatLng.longitude, TRIGGER_RADIUS)
+ .setRequestId(city)
+ .setTransitionTypes(TRIGGER_TRANSITION)
+ .setExpirationDuration(EXPIRATION_DURATION)
+ .build());
+ }
+ return geofenceList;
+ }
+
+ public static String getClosestCity(LatLng curLatLng) {
+ if (curLatLng == null) {
+ // In debug build still return a city so some data is displayed
+ if (BuildConfig.DEBUG) {
+ return TEST_CITY;
+ }
+ return null;
+ }
+
+ double minDistance = 0;
+ String closestCity = null;
+ for (Map.Entry<String, LatLng> entry: CITY_LOCATIONS.entrySet()) {
+ double distance = SphericalUtil.computeDistanceBetween(curLatLng, entry.getValue());
+ if (minDistance == 0 || distance < minDistance) {
+ minDistance = distance;
+ closestCity = entry.getKey();
+ }
+ }
+ return closestCity;
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/ListenerService.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/ListenerService.java
new file mode 100644
index 0000000..c8a5790
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/ListenerService.java
@@ -0,0 +1,59 @@
+/*
+ * 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.service;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+import com.example.android.xyztouristattractions.ui.DetailActivity;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.WearableListenerService;
+import com.example.android.xyztouristattractions.common.Constants;
+
+/**
+ * A Wear listener service, used to receive inbound messages from
+ * the Wear device.
+ */
+public class ListenerService extends WearableListenerService {
+ private static final String TAG = ListenerService.class.getSimpleName();
+
+ @Override
+ public void onMessageReceived(MessageEvent messageEvent) {
+ Log.v(TAG, "onMessageReceived: " + messageEvent);
+
+ if (Constants.CLEAR_NOTIFICATIONS_PATH.equals(messageEvent.getPath())) {
+ // Request for this device to clear its notifications
+ UtilityService.clearNotification(this);
+ } else if (Constants.START_ATTRACTION_PATH.equals(messageEvent.getPath())) {
+ // Request for this device open the attraction detail screen
+ // to a specific tourist attraction
+ String attractionName = new String(messageEvent.getData());
+ Intent intent = DetailActivity.getLaunchIntent(this, attractionName);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ } else if (Constants.START_NAVIGATION_PATH.equals(messageEvent.getPath())) {
+ // Request for this device to start Maps walking navigation to
+ // specific tourist attraction
+ String attractionQuery = new String(messageEvent.getData());
+ Uri uri = Uri.parse(Constants.MAPS_NAVIGATION_INTENT_URI + Uri.encode(attractionQuery));
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/UtilityReceiver.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/UtilityReceiver.java
new file mode 100644
index 0000000..0a10ba2
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/UtilityReceiver.java
@@ -0,0 +1,41 @@
+/*
+ * 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.service;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.content.WakefulBroadcastReceiver;
+
+/**
+ * A simply utility receiver used to ensure the device stays awake for the
+ * duration of the work being done by
+ * {@link com.example.android.xyztouristattractions.service.UtilityService}.
+ */
+public class UtilityReceiver extends WakefulBroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Pass right over to UtilityService class, the wakeful receiver is
+ // just needed in case the geofence is triggered while the device
+ // is asleep otherwise the service may not have time to trigger the
+ // notification.
+ intent.setClass(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_GEOFENCE_TRIGGERED);
+ startWakefulService(context, intent);
+ }
+
+}
diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/UtilityService.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/UtilityService.java
new file mode 100644
index 0000000..4112a65
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/service/UtilityService.java
@@ -0,0 +1,485 @@
+/*
+ * 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.service;
+
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.location.Location;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.example.android.xyztouristattractions.R;
+import com.example.android.xyztouristattractions.common.Attraction;
+import com.example.android.xyztouristattractions.common.Constants;
+import com.example.android.xyztouristattractions.common.Utils;
+import com.example.android.xyztouristattractions.provider.TouristAttractions;
+import com.example.android.xyztouristattractions.ui.DetailActivity;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.FusedLocationProviderApi;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingEvent;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.PutDataMapRequest;
+import com.google.android.gms.wearable.PutDataRequest;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import static com.example.android.xyztouristattractions.provider.TouristAttractions.ATTRACTIONS;
+import static com.google.android.gms.location.LocationServices.FusedLocationApi;
+import static com.google.android.gms.location.LocationServices.GeofencingApi;
+
+/**
+ * A utility IntentService, used for a variety of asynchronous background
+ * operations that do not necessarily need to be tied to a UI.
+ */
+public class UtilityService extends IntentService {
+ private static final String TAG = UtilityService.class.getSimpleName();
+
+ public static final String ACTION_GEOFENCE_TRIGGERED = "geofence_triggered";
+ private static final String ACTION_LOCATION_UPDATED = "location_updated";
+ private static final String ACTION_REQUEST_LOCATION = "request_location";
+ private static final String ACTION_ADD_GEOFENCES = "add_geofences";
+ private static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
+ private static final String ACTION_CLEAR_REMOTE_NOTIFICATIONS = "clear_remote_notifications";
+ private static final String ACTION_FAKE_UPDATE = "fake_update";
+ private static final String EXTRA_TEST_MICROAPP = "test_microapp";
+
+ public static IntentFilter getLocationUpdatedIntentFilter() {
+ return new IntentFilter(UtilityService.ACTION_LOCATION_UPDATED);
+ }
+
+ public static void triggerWearTest(Context context, boolean microApp) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_FAKE_UPDATE);
+ intent.putExtra(EXTRA_TEST_MICROAPP, microApp);
+ context.startService(intent);
+ }
+
+ public static void addGeofences(Context context) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_ADD_GEOFENCES);
+ context.startService(intent);
+ }
+
+ public static void requestLocation(Context context) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_REQUEST_LOCATION);
+ context.startService(intent);
+ }
+
+ public static void clearNotification(Context context) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_CLEAR_NOTIFICATION);
+ context.startService(intent);
+ }
+
+ public static Intent getClearRemoteNotificationsIntent(Context context) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_CLEAR_REMOTE_NOTIFICATIONS);
+ return intent;
+ }
+
+ public UtilityService() {
+ super(TAG);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent != null ? intent.getAction() : null;
+ if (ACTION_ADD_GEOFENCES.equals(action)) {
+ addGeofencesInternal();
+ } else if (ACTION_GEOFENCE_TRIGGERED.equals(action)) {
+ geofenceTriggered(intent);
+ } else if (ACTION_REQUEST_LOCATION.equals(action)) {
+ requestLocationInternal();
+ } else if (ACTION_LOCATION_UPDATED.equals(action)) {
+ locationUpdated(intent);
+ } else if (ACTION_CLEAR_NOTIFICATION.equals(action)) {
+ clearNotificationInternal();
+ } else if (ACTION_CLEAR_REMOTE_NOTIFICATIONS.equals(action)) {
+ clearRemoteNotifications();
+ } else if (ACTION_FAKE_UPDATE.equals(action)) {
+ LatLng currentLocation = Utils.getLocation(this);
+
+ // If location unknown use test city, otherwise use closest city
+ String city = currentLocation == null ? TouristAttractions.TEST_CITY :
+ TouristAttractions.getClosestCity(currentLocation);
+
+ showNotification(city,
+ intent.getBooleanExtra(EXTRA_TEST_MICROAPP, Constants.USE_MICRO_APP));
+ }
+ }
+
+ /**
+ * Add geofences using Play Services
+ */
+ private void addGeofencesInternal() {
+ Log.v(TAG, ACTION_ADD_GEOFENCES);
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(LocationServices.API)
+ .build();
+
+ // It's OK to use blockingConnect() here as we are running in an
+ // IntentService that executes work on a separate (background) thread.
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ this, 0, new Intent(this, UtilityReceiver.class), 0);
+ GeofencingApi.addGeofences(googleApiClient,
+ TouristAttractions.getGeofenceList(), pendingIntent);
+ googleApiClient.disconnect();
+ } else {
+ Log.e(TAG, String.format(Constants.GOOGLE_API_CLIENT_ERROR_MSG,
+ connectionResult.getErrorCode()));
+ }
+ }
+
+ /**
+ * Called when a geofence is triggered
+ */
+ private void geofenceTriggered(Intent intent) {
+ Log.v(TAG, ACTION_GEOFENCE_TRIGGERED);
+
+ // Check if geofences are enabled
+ boolean geofenceEnabled = Utils.getGeofenceEnabled(this);
+
+ // Extract the geofences from the intent
+ GeofencingEvent event = GeofencingEvent.fromIntent(intent);
+ List<Geofence> geofences = event.getTriggeringGeofences();
+
+ if (geofenceEnabled && geofences != null && geofences.size() > 0) {
+ if (event.getGeofenceTransition() == Geofence.GEOFENCE_TRANSITION_ENTER) {
+ // Trigger the notification based on the first geofence
+ showNotification(geofences.get(0).getRequestId(), Constants.USE_MICRO_APP);
+ } else if (event.getGeofenceTransition() == Geofence.GEOFENCE_TRANSITION_EXIT) {
+ // Clear notifications
+ clearNotificationInternal();
+ clearRemoteNotifications();
+ }
+ }
+ UtilityReceiver.completeWakefulIntent(intent);
+ }
+
+ /**
+ * Called when a location update is requested
+ */
+ private void requestLocationInternal() {
+ Log.v(TAG, ACTION_REQUEST_LOCATION);
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(LocationServices.API)
+ .build();
+
+ // It's OK to use blockingConnect() here as we are running in an
+ // IntentService that executes work on a separate (background) thread.
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
+
+ Intent locationUpdatedIntent = new Intent(this, UtilityService.class);
+ locationUpdatedIntent.setAction(ACTION_LOCATION_UPDATED);
+
+ // Send last known location out first if available
+ Location location = FusedLocationApi.getLastLocation(googleApiClient);
+ if (location != null) {
+ Intent lastLocationIntent = new Intent(locationUpdatedIntent);
+ lastLocationIntent.putExtra(
+ FusedLocationProviderApi.KEY_LOCATION_CHANGED, location);
+ startService(lastLocationIntent);
+ }
+
+ // Request new location
+ LocationRequest mLocationRequest = new LocationRequest()
+ .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
+ FusedLocationApi.requestLocationUpdates(
+ googleApiClient, mLocationRequest,
+ PendingIntent.getService(this, 0, locationUpdatedIntent, 0));
+
+ googleApiClient.disconnect();
+ } else {
+ Log.e(TAG, String.format(Constants.GOOGLE_API_CLIENT_ERROR_MSG,
+ connectionResult.getErrorCode()));
+ }
+ }
+
+ /**
+ * Called when the location has been updated
+ */
+ private void locationUpdated(Intent intent) {
+ Log.v(TAG, ACTION_LOCATION_UPDATED);
+
+ // Extra new location
+ Location location =
+ intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
+
+ if (location != null) {
+ LatLng latLngLocation = new LatLng(location.getLatitude(), location.getLongitude());
+
+ // Store in a local preference as well
+ Utils.storeLocation(this, latLngLocation);
+
+ // Send a local broadcast so if an Activity is open it can respond
+ // to the updated location
+ LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
+ }
+ }
+
+ /**
+ * Clears the local device notification
+ */
+ private void clearNotificationInternal() {
+ Log.v(TAG, ACTION_CLEAR_NOTIFICATION);
+ NotificationManagerCompat.from(this).cancel(Constants.MOBILE_NOTIFICATION_ID);
+ }
+
+ /**
+ * Clears remote device notifications using the Wearable message API
+ */
+ private void clearRemoteNotifications() {
+ Log.v(TAG, ACTION_CLEAR_REMOTE_NOTIFICATIONS);
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .build();
+
+ // It's OK to use blockingConnect() here as we are running in an
+ // IntentService that executes work on a separate (background) thread.
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
+
+ // Loop through all nodes and send a clear notification message
+ Iterator<String> itr = Utils.getNodes(googleApiClient).iterator();
+ while (itr.hasNext()) {
+ Wearable.MessageApi.sendMessage(
+ googleApiClient, itr.next(), Constants.CLEAR_NOTIFICATIONS_PATH, null);
+ }
+ googleApiClient.disconnect();
+ }
+ }
+
+
+ /**
+ * Show the notification. Either the regular notification with wearable features
+ * added to enhance, or trigger the full micro app on the wearable.
+ *
+ * @param cityId The city to trigger the notification for
+ * @param microApp If the micro app should be triggered or just enhanced notifications
+ */
+ private void showNotification(String cityId, boolean microApp) {
+
+ List<Attraction> attractions = ATTRACTIONS.get(cityId);
+
+ if (microApp) {
+ // If micro app we first need to transfer some data over
+ sendDataToWearable(attractions);
+ }
+
+ // The first (closest) tourist attraction
+ Attraction attraction = attractions.get(0);
+
+ // Limit attractions to send
+ int count = attractions.size() > Constants.MAX_ATTRACTIONS ?
+ Constants.MAX_ATTRACTIONS : attractions.size();
+
+ // Pull down the tourist attraction images from the network and store
+ HashMap<String, Bitmap> bitmaps = new HashMap<>();
+ try {
+ for (int i = 0; i < count; i++) {
+ bitmaps.put(attractions.get(i).name,
+ Glide.with(this)
+ .load(attractions.get(i).imageUrl)
+ .asBitmap()
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .into(Constants.WEAR_IMAGE_SIZE, Constants.WEAR_IMAGE_SIZE)
+ .get());
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error fetching image from network: " + e);
+ }
+
+ // The intent to trigger when the notification is tapped
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
+ DetailActivity.getLaunchIntent(this, attraction.name),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ // The intent to trigger when the notification is dismissed, in this case
+ // we want to clear remote notifications as well
+ PendingIntent deletePendingIntent =
+ PendingIntent.getService(this, 0, getClearRemoteNotificationsIntent(this), 0);
+
+ // Construct the main notification
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
+ .setStyle(new NotificationCompat.BigPictureStyle()
+ .bigPicture(bitmaps.get(attraction.name))
+ .setBigContentTitle(attraction.name)
+ .setSummaryText(getString(R.string.nearby_attraction))
+ )
+ .setLocalOnly(microApp)
+ .setContentTitle(attraction.name)
+ .setContentText(getString(R.string.nearby_attraction))
+ .setSmallIcon(R.drawable.ic_stat_maps_pin_drop)
+ .setContentIntent(pendingIntent)
+ .setDeleteIntent(deletePendingIntent)
+ .setColor(getResources().getColor(R.color.colorPrimary))
+ .setCategory(Notification.CATEGORY_RECOMMENDATION)
+ .setAutoCancel(true);
+
+ if (!microApp) {
+ // If not a micro app, create some wearable pages for
+ // the other nearby tourist attractions.
+ ArrayList<Notification> pages = new ArrayList<Notification>();
+ for (int i = 1; i < count; i++) {
+
+ // Calculate the distance from current location to tourist attraction
+ String distance = Utils.formatDistanceBetween(
+ Utils.getLocation(this), attractions.get(i).location);
+
+ // Construct the notification and add it as a page
+ pages.add(new NotificationCompat.Builder(this)
+ .setContentTitle(attractions.get(i).name)
+ .setContentText(distance)
+ .setSmallIcon(R.drawable.ic_stat_maps_pin_drop)
+ .extend(new NotificationCompat.WearableExtender()
+ .setBackground(bitmaps.get(attractions.get(i).name))
+ )
+ .build());
+ }
+ builder.extend(new NotificationCompat.WearableExtender().addPages(pages));
+ }
+
+ // Trigger the notification
+ NotificationManagerCompat.from(this).notify(
+ Constants.MOBILE_NOTIFICATION_ID, builder.build());
+ }
+
+ /**
+ * Transfer the required data over to the wearable
+ * @param attractions list of attraction data to transfer over
+ */
+ private void sendDataToWearable(List<Attraction> attractions) {
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .build();
+
+ // It's OK to use blockingConnect() here as we are running in an
+ // IntentService that executes work on a separate (background) thread.
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ // Limit attractions to send
+ int count = attractions.size() > Constants.MAX_ATTRACTIONS ?
+ Constants.MAX_ATTRACTIONS : attractions.size();
+
+ ArrayList<DataMap> attractionsData = new ArrayList<>(count);
+
+ for (int i = 0; i < count; i++) {
+ Attraction attraction = attractions.get(i);
+
+ Bitmap image = null;
+ Bitmap secondaryImage = null;
+
+ try {
+ // Fetch and resize attraction image bitmap
+ image = Glide.with(this)
+ .load(attraction.imageUrl)
+ .asBitmap()
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .into(Constants.WEAR_IMAGE_SIZE_PARALLAX_WIDTH, Constants.WEAR_IMAGE_SIZE)
+ .get();
+
+ secondaryImage = Glide.with(this)
+ .load(attraction.secondaryImageUrl)
+ .asBitmap()
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .into(Constants.WEAR_IMAGE_SIZE_PARALLAX_WIDTH, Constants.WEAR_IMAGE_SIZE)
+ .get();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Exception loading bitmap from network");
+ }
+
+ if (image != null && secondaryImage != null) {
+
+ DataMap attractionData = new DataMap();
+
+ String distance = Utils.formatDistanceBetween(
+ Utils.getLocation(this), attraction.location);
+
+ attractionData.putString(Constants.EXTRA_TITLE, attraction.name);
+ attractionData.putString(Constants.EXTRA_DESCRIPTION, attraction.description);
+ attractionData.putDouble(
+ Constants.EXTRA_LOCATION_LAT, attraction.location.latitude);
+ attractionData.putDouble(
+ Constants.EXTRA_LOCATION_LNG, attraction.location.longitude);
+ attractionData.putString(Constants.EXTRA_DISTANCE, distance);
+ attractionData.putString(Constants.EXTRA_CITY, attraction.city);
+ attractionData.putAsset(Constants.EXTRA_IMAGE,
+ Utils.createAssetFromBitmap(image));
+ attractionData.putAsset(Constants.EXTRA_IMAGE_SECONDARY,
+ Utils.createAssetFromBitmap(secondaryImage));
+
+ attractionsData.add(attractionData);
+ }
+ }
+
+ if (connectionResult.isSuccess() && googleApiClient.isConnected()
+ && attractionsData.size() > 0) {
+
+ PutDataMapRequest dataMap = PutDataMapRequest.create(Constants.ATTRACTION_PATH);
+ dataMap.getDataMap().putDataMapArrayList(Constants.EXTRA_ATTRACTIONS, attractionsData);
+ dataMap.getDataMap().putLong(Constants.EXTRA_TIMESTAMP, new Date().getTime());
+ PutDataRequest request = dataMap.asPutDataRequest();
+
+ // Send the data over
+ DataApi.DataItemResult result =
+ Wearable.DataApi.putDataItem(googleApiClient, request).await();
+
+ if (!result.getStatus().isSuccess()) {
+ Log.e(TAG, String.format("Error sending data using DataApi (error code = %d)",
+ result.getStatus().getStatusCode()));
+ }
+
+ } else {
+ Log.e(TAG, String.format(Constants.GOOGLE_API_CLIENT_ERROR_MSG,
+ connectionResult.getErrorCode()));
+ }
+ googleApiClient.disconnect();
+ }
+}
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
new file mode 100644
index 0000000..8d43112
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListActivity.java
@@ -0,0 +1,101 @@
+/*
+ * 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.ui;
+
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.example.android.xyztouristattractions.R;
+import com.example.android.xyztouristattractions.common.Utils;
+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 {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ if (savedInstanceState == null) {
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.container, new AttractionListFragment())
+ .commit();
+ }
+
+ UtilityService.addGeofences(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ UtilityService.requestLocation(this);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ switch (item.getItemId()) {
+ case R.id.test_notification:
+ UtilityService.triggerWearTest(this, false);
+ showDebugDialog(R.string.action_test_notification,
+ R.string.action_test_notification_dialog);
+ return true;
+ case R.id.test_microapp:
+ UtilityService.triggerWearTest(this, true);
+ showDebugDialog(R.string.action_test_microapp,
+ R.string.action_test_microapp_dialog);
+ return true;
+ case R.id.test_toggle_geofence:
+ boolean geofenceEnabled = Utils.getGeofenceEnabled(this);
+ Utils.storeGeofenceEnabled(this, !geofenceEnabled);
+ Toast.makeText(this, geofenceEnabled ?
+ "Debug: Geofencing trigger disabled" :
+ "Debug: Geofencing trigger enabled", Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Show a basic debug dialog to provide more info on the built-in debug
+ * options.
+ */
+ private void showDebugDialog(int titleResId, int bodyResId) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(titleResId)
+ .setMessage(bodyResId)
+ .setPositiveButton(android.R.string.ok, null);
+ builder.create().show();
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListFragment.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListFragment.java
new file mode 100644
index 0000000..0f1bc8b
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionListFragment.java
@@ -0,0 +1,226 @@
+/*
+ * 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.ui;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Location;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.example.android.xyztouristattractions.R;
+import com.example.android.xyztouristattractions.common.Attraction;
+import com.example.android.xyztouristattractions.common.Constants;
+import com.example.android.xyztouristattractions.common.Utils;
+import com.example.android.xyztouristattractions.provider.TouristAttractions;
+import com.example.android.xyztouristattractions.service.UtilityService;
+import com.google.android.gms.location.FusedLocationProviderApi;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.maps.android.SphericalUtil;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static com.example.android.xyztouristattractions.provider.TouristAttractions.ATTRACTIONS;
+
+/**
+ * The main tourist attraction fragment which contains a list of attractions
+ * sorted by distance (contained inside
+ * {@link com.example.android.xyztouristattractions.ui.AttractionListActivity}).
+ */
+public class AttractionListFragment extends Fragment {
+
+ private AttractionAdapter mAdapter;
+ private LatLng mLatestLocation;
+ private int mImageSize;
+
+ public AttractionListFragment() {}
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Load a larger size image to make the activity transition to the detail screen smooth
+ mImageSize = getResources().getDimensionPixelSize(R.dimen.image_size)
+ * Constants.IMAGE_ANIM_MULTIPLIER;
+
+ mLatestLocation = Utils.getLocation(getActivity());
+ List<Attraction> attractions = loadAttractionsFromLocation(mLatestLocation);
+ mAdapter = new AttractionAdapter(getActivity(), attractions);
+
+ View view = inflater.inflate(R.layout.fragment_main, container, false);
+ AttractionsRecyclerView recyclerView =
+ (AttractionsRecyclerView) view.findViewById(android.R.id.list);
+ recyclerView.setEmptyView(view.findViewById(android.R.id.empty));
+ recyclerView.setHasFixedSize(true);
+ recyclerView.setLayoutManager(new GridLayoutManager(
+ getActivity(), getResources().getInteger(R.integer.list_columns)));
+ recyclerView.setAdapter(mAdapter);
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
+ mBroadcastReceiver, UtilityService.getLocationUpdatedIntentFilter());
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mBroadcastReceiver);
+ }
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Location location =
+ intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
+ if (location != null) {
+ mLatestLocation = new LatLng(location.getLatitude(), location.getLongitude());
+ mAdapter.mAttractionList = loadAttractionsFromLocation(mLatestLocation);
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ private static List<Attraction> loadAttractionsFromLocation(final LatLng curLatLng) {
+ String closestCity = TouristAttractions.getClosestCity(curLatLng);
+ if (closestCity != null) {
+ List<Attraction> attractions = ATTRACTIONS.get(closestCity);
+ if (curLatLng != null) {
+ Collections.sort(attractions,
+ new Comparator<Attraction>() {
+ @Override
+ public int compare(Attraction lhs, Attraction rhs) {
+ double lhsDistance = SphericalUtil.computeDistanceBetween(
+ lhs.location, curLatLng);
+ double rhsDistance = SphericalUtil.computeDistanceBetween(
+ rhs.location, curLatLng);
+ return (int) (lhsDistance - rhsDistance);
+ }
+ }
+ );
+ }
+ return attractions;
+ }
+ return null;
+ }
+
+ private class AttractionAdapter extends RecyclerView.Adapter<ViewHolder>
+ implements ItemClickListener {
+
+ public List<Attraction> mAttractionList;
+ private Context mContext;
+
+ public AttractionAdapter(Context context, List<Attraction> attractions) {
+ super();
+ mContext = context;
+ mAttractionList = attractions;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.list_row, parent, false);
+ return new ViewHolder(view, this);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ Attraction attraction = mAttractionList.get(position);
+
+ holder.mTitleTextView.setText(attraction.name);
+ holder.mDescriptionTextView.setText(attraction.description);
+ Glide.with(mContext)
+ .load(attraction.imageUrl)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .placeholder(R.drawable.empty_photo)
+ .override(mImageSize, mImageSize)
+ .into(holder.mImageView);
+
+ String distance =
+ Utils.formatDistanceBetween(mLatestLocation, attraction.location);
+ if (TextUtils.isEmpty(distance)) {
+ holder.mOverlayTextView.setVisibility(View.GONE);
+ } else {
+ holder.mOverlayTextView.setVisibility(View.VISIBLE);
+ holder.mOverlayTextView.setText(distance);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mAttractionList == null ? 0 : mAttractionList.size();
+ }
+
+ @Override
+ public void onItemClick(View view, int position) {
+ View heroView = view.findViewById(android.R.id.icon);
+ DetailActivity.launch(
+ getActivity(), mAdapter.mAttractionList.get(position).name, heroView);
+ }
+ }
+
+ private static class ViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ TextView mTitleTextView;
+ TextView mDescriptionTextView;
+ TextView mOverlayTextView;
+ ImageView mImageView;
+ ItemClickListener mItemClickListener;
+
+ public ViewHolder(View view, ItemClickListener itemClickListener) {
+ super(view);
+ mTitleTextView = (TextView) view.findViewById(android.R.id.text1);
+ mDescriptionTextView = (TextView) view.findViewById(android.R.id.text2);
+ mOverlayTextView = (TextView) view.findViewById(R.id.overlaytext);
+ mImageView = (ImageView) view.findViewById(android.R.id.icon);
+ mItemClickListener = itemClickListener;
+ view.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ mItemClickListener.onItemClick(v, getPosition());
+ }
+ }
+
+ interface ItemClickListener {
+ void onItemClick(View view, int position);
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionsRecyclerView.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionsRecyclerView.java
new file mode 100644
index 0000000..cca3bf1
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/AttractionsRecyclerView.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ui;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Simple RecyclerView subclass that supports providing an empty view (which
+ * is displayed when the adapter has no data and hidden otherwise).
+ */
+public class AttractionsRecyclerView extends RecyclerView {
+ private View mEmptyView;
+
+ private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ updateEmptyView();
+ }
+ };
+
+ public AttractionsRecyclerView(Context context) {
+ super(context);
+ }
+
+ public AttractionsRecyclerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AttractionsRecyclerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Designate a view as the empty view. When the backing adapter has no
+ * data this view will be made visible and the recycler view hidden.
+ *
+ */
+ public void setEmptyView(View emptyView) {
+ mEmptyView = emptyView;
+ }
+
+ @Override
+ public void setAdapter(RecyclerView.Adapter adapter) {
+ if (getAdapter() != null) {
+ getAdapter().unregisterAdapterDataObserver(mDataObserver);
+ }
+ if (adapter != null) {
+ adapter.registerAdapterDataObserver(mDataObserver);
+ }
+ super.setAdapter(adapter);
+ updateEmptyView();
+ }
+
+ private void updateEmptyView() {
+ if (mEmptyView != null && getAdapter() != null) {
+ boolean showEmptyView = getAdapter().getItemCount() == 0;
+ mEmptyView.setVisibility(showEmptyView ? VISIBLE : GONE);
+ setVisibility(showEmptyView ? GONE : VISIBLE);
+ }
+ }
+}
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
new file mode 100644
index 0000000..a83f480
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailActivity.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ui;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+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.view.View;
+
+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 {
+
+ private static final String EXTRA_ATTRACTION = "attraction";
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public static void launch(Activity activity, String attraction, View heroView) {
+ Intent intent = getLaunchIntent(activity, attraction);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
+ activity, heroView, heroView.getTransitionName());
+ ActivityCompat.startActivity(activity, intent, options.toBundle());
+ } else {
+ activity.startActivity(intent);
+ }
+ }
+
+ public static Intent getLaunchIntent(Context context, String attraction) {
+ Intent intent = new Intent(context, DetailActivity.class);
+ intent.putExtra(EXTRA_ATTRACTION, attraction);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ String attraction = getIntent().getStringExtra(EXTRA_ATTRACTION);
+ if (savedInstanceState == null) {
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.container, DetailFragment.createInstance(attraction))
+ .commit();
+ }
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailFragment.java b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailFragment.java
new file mode 100644
index 0000000..4d21009
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/src/com.example.android.xyztouristattractions/ui/DetailFragment.java
@@ -0,0 +1,167 @@
+/*
+ * 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.ui;
+
+import android.app.TaskStackBuilder;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.NavUtils;
+import android.text.TextUtils;
+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.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.example.android.xyztouristattractions.R;
+import com.example.android.xyztouristattractions.common.Attraction;
+import com.example.android.xyztouristattractions.common.Constants;
+import com.example.android.xyztouristattractions.common.Utils;
+import com.google.android.gms.maps.model.LatLng;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.example.android.xyztouristattractions.provider.TouristAttractions.ATTRACTIONS;
+
+/**
+ * The tourist attraction detail fragment which contains the details of a
+ * a single attraction (contained inside
+ * {@link com.example.android.xyztouristattractions.ui.DetailActivity}).
+ */
+public class DetailFragment extends Fragment {
+
+ private static final String EXTRA_ATTRACTION = "attraction";
+ private Attraction mAttraction;
+
+ public static DetailFragment createInstance(String attractionName) {
+ DetailFragment detailFragment = new DetailFragment();
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_ATTRACTION, attractionName);
+ detailFragment.setArguments(bundle);
+ return detailFragment;
+ }
+
+ public DetailFragment() {}
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ setHasOptionsMenu(true);
+ View view = inflater.inflate(R.layout.fragment_detail, container, false);
+ String attractionName = getArguments().getString(EXTRA_ATTRACTION);
+ mAttraction = findAttraction(attractionName);
+
+ if (mAttraction == null) {
+ getActivity().finish();
+ return null;
+ }
+
+ TextView nameTextView = (TextView) view.findViewById(R.id.nameTextView);
+ TextView descTextView = (TextView) view.findViewById(R.id.descriptionTextView);
+ TextView distanceTextView = (TextView) view.findViewById(R.id.distanceTextView);
+ ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
+
+ LatLng location = Utils.getLocation(getActivity());
+ String distance = Utils.formatDistanceBetween(location, mAttraction.location);
+ if (TextUtils.isEmpty(distance)) {
+ distanceTextView.setVisibility(View.GONE);
+ }
+
+ nameTextView.setText(attractionName);
+ distanceTextView.setText(distance);
+ descTextView.setText(mAttraction.longDescription);
+
+ int imageSize = getResources().getDimensionPixelSize(R.dimen.image_size)
+ * Constants.IMAGE_ANIM_MULTIPLIER;
+ Glide.with(getActivity())
+ .load(mAttraction.imageUrl)
+ .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+ .placeholder(R.color.lighter_gray)
+ .override(imageSize, imageSize)
+ .into(imageView);
+ return view;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.detail, menu);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ // Some small additions to handle "up" navigation correctly
+ Intent upIntent = NavUtils.getParentActivityIntent(getActivity());
+ upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ // Check if up activity needs to be created (usually when
+ // detail screen is opened from a notification or from the
+ // Wearable app
+ if (NavUtils.shouldUpRecreateTask(getActivity(), upIntent)
+ || getActivity().isTaskRoot()) {
+
+ // Synthesize parent stack
+ TaskStackBuilder.create(getActivity())
+ .addNextIntentWithParentStack(upIntent)
+ .startActivities();
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // On Lollipop+ we finish so to run the nice animation
+ getActivity().finishAfterTransition();
+ return true;
+ }
+
+ // Otherwise let the system handle navigating "up"
+ return false;
+ case R.id.map:
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(Constants.MAPS_INTENT_URI +
+ Uri.encode(mAttraction.name + ", " + mAttraction.city)));
+ startActivity(intent);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Really hacky loop for finding attraction in our static content provider.
+ * Obviously would not be used in a production app.
+ */
+ private Attraction findAttraction(String attractionName) {
+ for (Map.Entry<String, List<Attraction>> attractionsList : ATTRACTIONS.entrySet()) {
+ List<Attraction> attractions = attractionsList.getValue();
+ for (Attraction attraction : attractions) {
+ if (attractionName.equals(attraction.name)) {
+ return attraction;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml b/samples/browseable/XYZTouristAttractions/Shared/AndroidManifest.xml
similarity index 63%
copy from samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml
copy to samples/browseable/XYZTouristAttractions/Shared/AndroidManifest.xml
index 8c1ea66..fe07629 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-v11/template-styles.xml
+++ b/samples/browseable/XYZTouristAttractions/Shared/AndroidManifest.xml
@@ -1,11 +1,11 @@
<!--
- Copyright 2013 The Android Open Source Project
+ 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
+ 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,
@@ -14,9 +14,9 @@
limitations under the License.
-->
-<resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.xyztouristattractions.common">
- <!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+ <application android:allowBackup="true"/>
-</resources>
+</manifest>
diff --git a/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Attraction.java b/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Attraction.java
new file mode 100644
index 0000000..d65359c
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Attraction.java
@@ -0,0 +1,53 @@
+/*
+ * 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.common;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+
+import com.google.android.gms.maps.model.LatLng;
+
+/**
+ * A simple shared tourist attraction class to easily pass data around. Used
+ * in both the mobile app and wearable app.
+ */
+public class Attraction {
+ public String name;
+ public String description;
+ public String longDescription;
+ public Uri imageUrl;
+ public Uri secondaryImageUrl;
+ public LatLng location;
+ public String city;
+
+ public Bitmap image;
+ public Bitmap secondaryImage;
+ public String distance;
+
+ public Attraction() {}
+
+ public Attraction(String name, String description, String longDescription, Uri imageUrl,
+ Uri secondaryImageUrl, LatLng location, String city) {
+ this.name = name;
+ this.description = description;
+ this.longDescription = longDescription;
+ this.imageUrl = imageUrl;
+ this.secondaryImageUrl = secondaryImageUrl;
+ this.location = location;
+ this.city = city;
+ }
+}
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Constants.java b/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Constants.java
new file mode 100644
index 0000000..4d96436
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Constants.java
@@ -0,0 +1,73 @@
+/*
+ * 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.common;
+
+public class Constants {
+
+ private Constants() {};
+
+ // Set to false to have the geofence trigger use the enhanced notifications instead
+ public static final boolean USE_MICRO_APP = true;
+
+ public static final int GOOGLE_API_CLIENT_TIMEOUT_S = 10; // 10 seconds
+ public static final String GOOGLE_API_CLIENT_ERROR_MSG =
+ "Failed to connect to GoogleApiClient (error code = %d)";
+
+ // Used to size the images in the mobile app so they can animate cleanly from list to detail
+ public static final int IMAGE_ANIM_MULTIPLIER = 2;
+
+ // Resize images sent to Wear to 400x400px
+ public static final int WEAR_IMAGE_SIZE = 400;
+
+ // Except images that can be set as a background with parallax, set width 640x instead
+ public static final int WEAR_IMAGE_SIZE_PARALLAX_WIDTH = 640;
+
+ // The minimum bottom inset percent to use on a round screen device
+ public static final float WEAR_ROUND_MIN_INSET_PERCENT = 0.08f;
+
+ // Max # of attractions to show at once
+ public static final int MAX_ATTRACTIONS = 4;
+
+ // Notification IDs
+ public static final int MOBILE_NOTIFICATION_ID = 100;
+ public static final int WEAR_NOTIFICATION_ID = 200;
+
+ // Intent and bundle extras
+ public static final String EXTRA_ATTRACTIONS = "extra_attractions";
+ public static final String EXTRA_ATTRACTIONS_URI = "extra_attractions_uri";
+ public static final String EXTRA_TITLE = "extra_title";
+ public static final String EXTRA_DESCRIPTION = "extra_description";
+ public static final String EXTRA_LOCATION_LAT = "extra_location_lat";
+ public static final String EXTRA_LOCATION_LNG = "extra_location_lng";
+ public static final String EXTRA_DISTANCE = "extra_distance";
+ public static final String EXTRA_CITY = "extra_city";
+ public static final String EXTRA_IMAGE = "extra_image";
+ public static final String EXTRA_IMAGE_SECONDARY = "extra_image_secondary";
+ public static final String EXTRA_TIMESTAMP = "extra_timestamp";
+
+ // Wear Data API paths
+ public static final String ATTRACTION_PATH = "/attraction";
+ public static final String START_PATH = "/start";
+ public static final String START_ATTRACTION_PATH = START_PATH + "/attraction";
+ public static final String START_NAVIGATION_PATH = START_PATH + "/navigation";
+ public static final String CLEAR_NOTIFICATIONS_PATH = "/clear";
+
+ // Maps values
+ public static final String MAPS_INTENT_URI = "geo:0,0?q=";
+ public static final String MAPS_NAVIGATION_INTENT_URI = "google.navigation:mode=w&q=";
+
+}
diff --git a/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Utils.java b/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Utils.java
new file mode 100644
index 0000000..70e05bf
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Shared/src/com.example.android.xyztouristattractions.common/Utils.java
@@ -0,0 +1,200 @@
+/*
+ * 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.common;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.Display;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.NodeApi;
+import com.google.android.gms.wearable.Wearable;
+import com.google.maps.android.SphericalUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.text.NumberFormat;
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * This class contains shared static utility methods that both the mobile and
+ * wearable apps can use.
+ */
+public class Utils {
+ private static final String TAG = Utils.class.getSimpleName();
+
+ private static final String PREFERENCES_LAT = "lat";
+ private static final String PREFERENCES_LNG = "lng";
+ private static final String PREFERENCES_GEOFENCE_ENABLED = "geofence";
+ private static final String DISTANCE_KM_POSTFIX = "km";
+ private static final String DISTANCE_M_POSTFIX = "m";
+
+ /**
+ * Calculate distance between two LatLng points and format it nicely for
+ * display. As this is a sample, it only statically supports metric units.
+ * A production app should check locale and support the correct units.
+ */
+ public static String formatDistanceBetween(LatLng point1, LatLng point2) {
+ if (point1 == null || point2 == null) {
+ return null;
+ }
+
+ NumberFormat numberFormat = NumberFormat.getNumberInstance();
+ double distance = Math.round(SphericalUtil.computeDistanceBetween(point1, point2));
+
+ // Adjust to KM if M goes over 1000 (see javadoc of method for note
+ // on only supporting metric)
+ if (distance >= 1000) {
+ numberFormat.setMaximumFractionDigits(1);
+ return numberFormat.format(distance / 1000) + DISTANCE_KM_POSTFIX;
+ }
+ return numberFormat.format(distance) + DISTANCE_M_POSTFIX;
+ }
+
+ /**
+ * Store the location in the app preferences.
+ */
+ public static void storeLocation(Context context, LatLng location) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putLong(PREFERENCES_LAT, Double.doubleToRawLongBits(location.latitude));
+ editor.putLong(PREFERENCES_LNG, Double.doubleToRawLongBits(location.longitude));
+ editor.apply();
+ }
+
+ /**
+ * Fetch the location from app preferences.
+ */
+ public static LatLng getLocation(Context context) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ Long lat = prefs.getLong(PREFERENCES_LAT, Long.MAX_VALUE);
+ Long lng = prefs.getLong(PREFERENCES_LNG, Long.MAX_VALUE);
+ if (lat != Long.MAX_VALUE && lng != Long.MAX_VALUE) {
+ Double latDbl = Double.longBitsToDouble(lat);
+ Double lngDbl = Double.longBitsToDouble(lng);
+ return new LatLng(latDbl, lngDbl);
+ }
+ return null;
+ }
+
+ /**
+ * Store if geofencing triggers will show a notification in app preferences.
+ */
+ public static void storeGeofenceEnabled(Context context, boolean enable) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(PREFERENCES_GEOFENCE_ENABLED, enable);
+ editor.apply();
+ }
+
+ /**
+ * Retrieve if geofencing triggers should show a notification from app preferences.
+ */
+ public static boolean getGeofenceEnabled(Context context) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ return prefs.getBoolean(PREFERENCES_GEOFENCE_ENABLED, true);
+ }
+
+ /**
+ * Convert an asset into a bitmap object synchronously. Only call this
+ * method from a background thread (it should never be called from the
+ * main/UI thread as it blocks).
+ */
+ public static Bitmap loadBitmapFromAsset(GoogleApiClient googleApiClient, Asset asset) {
+ if (asset == null) {
+ throw new IllegalArgumentException("Asset must be non-null");
+ }
+ // convert asset into a file descriptor and block until it's ready
+ InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
+ googleApiClient, asset).await().getInputStream();
+
+ if (assetInputStream == null) {
+ Log.w(TAG, "Requested an unknown Asset.");
+ return null;
+ }
+ // decode the stream into a bitmap
+ return BitmapFactory.decodeStream(assetInputStream);
+ }
+
+ /**
+ * Create a wearable asset from a bitmap.
+ */
+ public static Asset createAssetFromBitmap(Bitmap bitmap) {
+ if (bitmap != null) {
+ final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
+ return Asset.createFromBytes(byteStream.toByteArray());
+ }
+ return null;
+ }
+
+ /**
+ * Get a list of all wearable nodes that are connected synchronously.
+ * Only call this method from a background thread (it should never be
+ * called from the main/UI thread as it blocks).
+ */
+ public static Collection<String> getNodes(GoogleApiClient client) {
+ Collection<String> results= new HashSet<String>();
+ NodeApi.GetConnectedNodesResult nodes =
+ Wearable.NodeApi.getConnectedNodes(client).await();
+ for (Node node : nodes.getNodes()) {
+ results.add(node.getId());
+ }
+ return results;
+ }
+
+ /**
+ * Calculates the square insets on a round device. If the system insets are not set
+ * (set to 0) then the inner square of the circle is applied instead.
+ *
+ * @param display device default display
+ * @param systemInsets the system insets
+ * @return adjusted square insets for use on a round device
+ */
+ public static Rect calculateBottomInsetsOnRoundDevice(Display display, Rect systemInsets) {
+ Point size = new Point();
+ display.getSize(size);
+ int width = size.x + systemInsets.left + systemInsets.right;
+ int height = size.y + systemInsets.top + systemInsets.bottom;
+
+ // Minimum inset to use on a round screen, calculated as a fixed percent of screen height
+ int minInset = (int) (height * Constants.WEAR_ROUND_MIN_INSET_PERCENT);
+
+ // Use system inset if it is larger than min inset, otherwise use min inset
+ int bottomInset = systemInsets.bottom > minInset ? systemInsets.bottom : minInset;
+
+ // Calculate left and right insets based on bottom inset
+ double radius = width / 2;
+ double apothem = radius - bottomInset;
+ double chord = Math.sqrt(Math.pow(radius, 2) - Math.pow(apothem, 2)) * 2;
+ int leftRightInset = (int) ((width - chord) / 2);
+
+ Log.d(TAG, "calculateBottomInsetsOnRoundDevice: " + bottomInset + ", " + leftRightInset);
+
+ return new Rect(leftRightInset, 0, leftRightInset, bottomInset);
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/AndroidManifest.xml b/samples/browseable/XYZTouristAttractions/Wearable/AndroidManifest.xml
new file mode 100644
index 0000000..24328b3
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.xyztouristattractions" >
+
+ <uses-feature android:name="android.hardware.type.watch" />
+
+ <uses-sdk
+ android:minSdkVersion="21"
+ android:targetSdkVersion="21" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.DeviceDefault" >
+
+ <activity
+ android:name=".ui.AttractionsActivity"
+ android:exported="true"
+ android:allowEmbedded="true"
+ android:taskAffinity=""
+ android:label="@string/app_name" />
+
+ <activity
+ android:name="android.support.wearable.activity.ConfirmationActivity"
+ android:theme="@android:style/Theme.Translucent" />
+
+ <service android:name=".service.ListenerService">
+ <intent-filter>
+ <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
+ </intent-filter>
+ </service>
+
+ <service android:name=".service.UtilityService" />
+
+ <meta-data android:name="com.google.android.gms.version"
+ android:value="@integer/google_play_services_version" />
+
+ </application>
+
+</manifest>
diff --git a/samples/browseable/FragmentTransition/res/layout/fragment_detail.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/color/action_color.xml
similarity index 60%
rename from samples/browseable/FragmentTransition/res/layout/fragment_detail.xml
rename to samples/browseable/XYZTouristAttractions/Wearable/res/color/action_color.xml
index d94256f..634d806 100644
--- a/samples/browseable/FragmentTransition/res/layout/fragment_detail.xml
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/color/action_color.xml
@@ -1,21 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright 2014 The Android Open Source Project
+ 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
+ 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.
--->
-<FrameLayout
- android:id="@+id/container"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"/>
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"
+ android:color="#ee3c4b90" /> <!-- pressed -->
+
+ <item android:color="#ee5c6bc0" /> <!-- default -->
+
+</selector>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_directions_walking.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_directions_walking.png
new file mode 100644
index 0000000..fdf7888
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_directions_walking.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_explore.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_explore.png
new file mode 100644
index 0000000..30353ef
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_explore.png
Binary files differ
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
new file mode 100644
index 0000000..4e11601
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-hdpi/ic_full_open_on_device.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_directions_walking.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_directions_walking.png
new file mode 100644
index 0000000..d61f241
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_directions_walking.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_explore.png b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_explore.png
new file mode 100644
index 0000000..57b85ae
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_explore.png
Binary files differ
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
new file mode 100644
index 0000000..2f6f056
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/drawable-xhdpi/ic_full_open_on_device.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/activity_main.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/activity_main.xml
new file mode 100644
index 0000000..a4ef94b
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/activity_main.xml
@@ -0,0 +1,55 @@
+<?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.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/topFrameLayout">
+
+ <ProgressBar
+ android:id="@+id/progressBar"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <android.support.wearable.view.GridViewPager
+ android:id="@+id/gridViewPager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" />
+
+ <android.support.wearable.view.DotsPageIndicator
+ android:id="@+id/dotsPageIndicator"
+ android:layout_gravity="center_horizontal|bottom"
+ 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
+ android:id="@+id/dismiss_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</FrameLayout>
+
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action.xml
new file mode 100644
index 0000000..4b3bbaf
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<android.support.wearable.view.WatchViewStub
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ 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" />
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_round.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_round.xml
new file mode 100644
index 0000000..70cec1a
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_round.xml
@@ -0,0 +1,59 @@
+<?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
new file mode 100644
index 0000000..362671b
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_action_square.xml
@@ -0,0 +1,63 @@
+<?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/layout/gridpager_card.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_card.xml
new file mode 100644
index 0000000..4b5f463
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_card.xml
@@ -0,0 +1,41 @@
+<?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.
+ -->
+
+<android.support.wearable.view.CardScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingBottom="@dimen/indicator_margin">
+
+ <android.support.wearable.view.CardFrame
+ android:id="@+id/cardFrame"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/CardFontStyle"
+ tools:text="Sample Text" />
+
+ </android.support.wearable.view.CardFrame>
+
+</android.support.wearable.view.CardScrollView>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_fullscreen_image.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_fullscreen_image.xml
new file mode 100644
index 0000000..e70bcc8
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/layout/gridpager_fullscreen_image.xml
@@ -0,0 +1,49 @@
+<?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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/imageView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop" />
+
+ <FrameLayout
+ android:id="@+id/overlaytext"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:background="#66000000">
+
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ style="@style/PagerTitleStyle"
+ android:maxLines="2"
+ android:layout_marginBottom="@dimen/indicator_margin"
+ tools:text="Sample Text" />
+
+ </FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/XYZTouristAttractions/Wearable/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..d159cbe
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/XYZTouristAttractions/Wearable/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..cc42dd2
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/values/dimens.xml
similarity index 61%
copy from samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
copy to samples/browseable/XYZTouristAttractions/Wearable/res/values/dimens.xml
index 22074a2..b07a27e 100644
--- a/samples/browseable/EmbeddedApp/Application/res/values-sw600dp/template-dimens.xml
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/values/dimens.xml
@@ -1,11 +1,11 @@
<!--
- Copyright 2013 The Android Open Source Project
+ 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
+ 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,
@@ -16,9 +16,11 @@
<resources>
- <!-- Semantic definitions -->
+ <dimen name="standard_margin">4dp</dimen>
+ <dimen name="card_margin">8dp</dimen>
+ <dimen name="indicator_margin">12dp</dimen>
- <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
- <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="page_row_margin">50dp</dimen>
+ <dimen name="page_column_margin">50dp</dimen>
</resources>
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/values/strings.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/values/strings.xml
new file mode 100644
index 0000000..2b265a3
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/values/strings.xml
@@ -0,0 +1,32 @@
+<?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.
+ -->
+
+<resources>
+
+ <string name="app_name">XYZ Tourist Attractions</string>
+ <string name="action_explore">Explore</string>
+ <string name="action_navigate">Start walking navigation</string>
+ <string name="action_open">Open on phone</string>
+ <string name="exit_intro_text">Long-press anywhere to exit</string>
+ <string name="map_caption">%s away</string>
+
+ <plurals name="attractions_found">
+ <item quantity="one">One tourist attraction nearby!</item>
+ <item quantity="other">%d tourist attractions nearby!</item>
+ </plurals>
+
+</resources>
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/res/values/styles.xml b/samples/browseable/XYZTouristAttractions/Wearable/res/values/styles.xml
new file mode 100644
index 0000000..cebe1c7
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/res/values/styles.xml
@@ -0,0 +1,48 @@
+<!--
+ 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.
+ -->
+
+<resources>
+
+ <style name="CardFontStyle" parent="@android:style/TextAppearance.Medium">
+ <item name="android:textColor">#434343</item>
+ <item name="android:fontFamily">sans-serif-condensed-light</item>
+ <item name="android:textSize">18sp</item>
+ <item name="android:padding">@dimen/card_margin</item>
+ </style>
+
+ <style name="PagerTitleStyle" parent="@android:style/TextAppearance.Large">
+ <item name="android:fontFamily">sans-serif-condensed-light</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">22sp</item>
+ <item name="android:maxLines">2</item>
+ <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/service/ListenerService.java b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/service/ListenerService.java
new file mode 100644
index 0000000..3908414
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/service/ListenerService.java
@@ -0,0 +1,131 @@
+/*
+ * 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.service;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.util.Log;
+
+import com.example.android.xyztouristattractions.R;
+import com.example.android.xyztouristattractions.common.Constants;
+import com.example.android.xyztouristattractions.common.Utils;
+import com.example.android.xyztouristattractions.ui.AttractionsActivity;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.data.FreezableUtils;
+import com.google.android.gms.wearable.DataEvent;
+import com.google.android.gms.wearable.DataEventBuffer;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.MessageEvent;
+import com.google.android.gms.wearable.Wearable;
+import com.google.android.gms.wearable.WearableListenerService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A Wear listener service, used to receive inbound messages from
+ * other devices.
+ */
+public class ListenerService extends WearableListenerService {
+ private static final String TAG = ListenerService.class.getSimpleName();
+
+ @Override
+ public void onDataChanged(DataEventBuffer dataEvents) {
+ Log.d(TAG, "onDataChanged: " + dataEvents);
+
+ final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
+
+ for (DataEvent event : events) {
+ if (event.getType() == DataEvent.TYPE_CHANGED
+ && event.getDataItem() != null
+ && Constants.ATTRACTION_PATH.equals(event.getDataItem().getUri().getPath())) {
+
+ DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
+ ArrayList<DataMap> attractionsData =
+ dataMapItem.getDataMap().getDataMapArrayList(Constants.EXTRA_ATTRACTIONS);
+ showNotification(dataMapItem.getUri(), attractionsData);
+ }
+ }
+ }
+
+ @Override
+ public void onMessageReceived(MessageEvent messageEvent) {
+ Log.v(TAG, "onMessageReceived: " + messageEvent);
+
+ if (Constants.CLEAR_NOTIFICATIONS_PATH.equals(messageEvent.getPath())) {
+ // Clear the local notification
+ UtilityService.clearNotification(this);
+ }
+ }
+
+ private void showNotification(Uri attractionsUri, ArrayList<DataMap> attractions) {
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .build();
+
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (!connectionResult.isSuccess() || !googleApiClient.isConnected()) {
+ Log.e(TAG, String.format(Constants.GOOGLE_API_CLIENT_ERROR_MSG,
+ connectionResult.getErrorCode()));
+ return;
+ }
+
+ Intent intent = new Intent(this, AttractionsActivity.class);
+ // Pass through the data Uri as an extra
+ intent.putExtra(Constants.EXTRA_ATTRACTIONS_URI, attractionsUri);
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ int count = attractions.size();
+
+ DataMap attraction = attractions.get(0);
+
+ Bitmap bitmap = Utils.loadBitmapFromAsset(
+ googleApiClient, attraction.getAsset(Constants.EXTRA_IMAGE));
+
+ PendingIntent deletePendingIntent = PendingIntent.getService(
+ this, 0, UtilityService.getClearRemoteNotificationsIntent(this), 0);
+
+ Notification notification = new Notification.Builder(this)
+ .setContentText(getResources().getQuantityString(
+ R.plurals.attractions_found, count, count))
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setDeleteIntent(deletePendingIntent)
+ .addAction(R.drawable.ic_full_explore,
+ getString(R.string.action_explore),
+ pendingIntent)
+ .extend(new Notification.WearableExtender()
+ .setBackground(bitmap)
+ )
+ .build();
+
+ NotificationManager notificationManager =
+ (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ notificationManager.notify(Constants.WEAR_NOTIFICATION_ID, notification);
+
+ googleApiClient.disconnect();
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/service/UtilityService.java b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/service/UtilityService.java
new file mode 100644
index 0000000..234002c
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/service/UtilityService.java
@@ -0,0 +1,158 @@
+/*
+ * 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.service;
+
+import android.app.IntentService;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+
+import com.example.android.xyztouristattractions.common.Constants;
+import com.example.android.xyztouristattractions.common.Utils;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A utility IntentService, used for a variety of asynchronous background
+ * operations that do not necessarily need to be tied to a UI.
+ */
+public class UtilityService extends IntentService {
+ private static final String TAG = UtilityService.class.getSimpleName();
+
+ private static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
+ private static final String ACTION_CLEAR_REMOTE_NOTIFICATIONS = "clear_remote_notifications";
+ private static final String ACTION_START_DEVICE_ACTIVITY = "start_device_activity";
+ private static final String EXTRA_START_PATH = "start_path";
+ private static final String EXTRA_START_ACTIVITY_INFO = "start_activity_info";
+
+ public static void clearNotification(Context context) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_CLEAR_NOTIFICATION);
+ context.startService(intent);
+ }
+
+ public static void clearRemoteNotifications(Context context) {
+ context.startService(getClearRemoteNotificationsIntent(context));
+ }
+
+ public static Intent getClearRemoteNotificationsIntent(Context context) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_CLEAR_REMOTE_NOTIFICATIONS);
+ return intent;
+ }
+
+ /**
+ * Trigger a message that asks the master device to start an activity.
+ *
+ * @param context the context
+ * @param path the path that will be sent via the wearable message API
+ * @param name the tourist attraction name
+ * @param city the tourist attraction city
+ */
+ public static void startDeviceActivity(Context context, String path, String name, String city) {
+ Intent intent = new Intent(context, UtilityService.class);
+ intent.setAction(UtilityService.ACTION_START_DEVICE_ACTIVITY);
+ String extraInfo;
+ if (Constants.START_ATTRACTION_PATH.equals(path)) {
+ extraInfo = name;
+ } else {
+ extraInfo = name + ", " + city;
+ }
+ intent.putExtra(EXTRA_START_ACTIVITY_INFO, extraInfo);
+ intent.putExtra(EXTRA_START_PATH, path);
+ context.startService(intent);
+ }
+
+ public UtilityService() {
+ super(TAG);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent != null ? intent.getAction() : null;
+ if (ACTION_CLEAR_NOTIFICATION.equals(action)) {
+ clearNotificationInternal();
+ } else if (ACTION_CLEAR_REMOTE_NOTIFICATIONS.equals(action)) {
+ clearRemoteNotificationsInternal();
+ } else if (ACTION_START_DEVICE_ACTIVITY.equals(action)) {
+ startDeviceActivityInternal(intent.getStringExtra(EXTRA_START_PATH),
+ intent.getStringExtra(EXTRA_START_ACTIVITY_INFO));
+ }
+ }
+
+ /**
+ * Clear the local notifications
+ */
+ private void clearNotificationInternal() {
+ NotificationManager notificationManager =
+ (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ notificationManager.cancel(Constants.WEAR_NOTIFICATION_ID);
+ }
+
+ /**
+ * Trigger a message to ask other devices to clear their notifications
+ */
+ private void clearRemoteNotificationsInternal() {
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .build();
+
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
+ Iterator<String> itr = Utils.getNodes(googleApiClient).iterator();
+ while (itr.hasNext()) {
+ // Loop through all connected nodes
+ Wearable.MessageApi.sendMessage(
+ googleApiClient, itr.next(), Constants.CLEAR_NOTIFICATIONS_PATH, null);
+ }
+ }
+
+ googleApiClient.disconnect();
+ }
+
+ /**
+ * Sends the actual message to ask other devices to start an activity
+ *
+ * @param path the path to pass to the wearable message API
+ * @param extraInfo extra info that varies based on the path being sent
+ */
+ private void startDeviceActivityInternal(String path, String extraInfo) {
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .build();
+
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
+ Iterator<String> itr = Utils.getNodes(googleApiClient).iterator();
+ while (itr.hasNext()) {
+ // Loop through all connected nodes
+ Wearable.MessageApi.sendMessage(
+ googleApiClient, itr.next(), path, extraInfo.getBytes());
+ }
+ }
+ googleApiClient.disconnect();
+ }
+
+}
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
new file mode 100644
index 0000000..464eb8a
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsActivity.java
@@ -0,0 +1,256 @@
+/*
+ * 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.ui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.view.GestureDetectorCompat;
+import android.support.wearable.view.DismissOverlayView;
+import android.support.wearable.view.DotsPageIndicator;
+import android.support.wearable.view.GridViewPager;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowInsets;
+import android.widget.FrameLayout;
+import android.widget.ProgressBar;
+
+import com.example.android.xyztouristattractions.R;
+import com.example.android.xyztouristattractions.common.Attraction;
+import com.example.android.xyztouristattractions.common.Constants;
+import com.example.android.xyztouristattractions.common.Utils;
+import com.example.android.xyztouristattractions.service.UtilityService;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.wearable.DataApi;
+import com.google.android.gms.wearable.DataMap;
+import com.google.android.gms.wearable.DataMapItem;
+import com.google.android.gms.wearable.Wearable;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The main Wear activity that displays nearby attractions in a
+ * {@link android.support.wearable.view.GridViewPager}. Each row shows
+ * one attraction and each column shows information or actions for that
+ * particular attraction.
+ */
+public class AttractionsActivity extends Activity
+ implements AttractionsGridPagerAdapter.OnChromeFadeListener {
+ private static final String TAG = AttractionsActivity.class.getSimpleName();
+
+ private GestureDetectorCompat mGestureDetector;
+ private DismissOverlayView mDismissOverlayView;
+ private GridViewPager mGridViewPager;
+ private AttractionsGridPagerAdapter mAdapter;
+ private DotsPageIndicator mDotsPageIndicator;
+ private ProgressBar mProgressBar;
+ private Rect mInsets = new Rect(0, 0, 0, 0);
+
+ private ArrayList<Attraction> mAttractions = new ArrayList<Attraction>();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+ final FrameLayout topFrameLayout = (FrameLayout) findViewById(R.id.topFrameLayout);
+ mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
+ mGridViewPager = (GridViewPager) findViewById(R.id.gridViewPager);
+ mDotsPageIndicator = (DotsPageIndicator) findViewById(R.id.dotsPageIndicator);
+ mAdapter = new AttractionsGridPagerAdapter(this, mAttractions);
+ mAdapter.setOnChromeFadeListener(this);
+ mGridViewPager.setAdapter(mAdapter);
+
+ topFrameLayout.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ // Call through to super implementation
+ insets = topFrameLayout.onApplyWindowInsets(insets);
+
+ boolean round = insets.isRound();
+
+ // Store system window insets regardless of screen shape
+ mInsets.set(insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ insets.getSystemWindowInsetBottom());
+
+ if (round) {
+ // On a round screen calculate the square inset to use.
+ // Alternatively could use BoxInsetLayout, although calculating
+ // the inset ourselves lets us position views outside the center
+ // box. For example, slightly lower on the round screen (by giving
+ // up some horizontal space).
+ mInsets = Utils.calculateBottomInsetsOnRoundDevice(
+ getWindowManager().getDefaultDisplay(), mInsets);
+
+ // Boost the dots indicator up by the bottom inset
+ FrameLayout.LayoutParams params =
+ (FrameLayout.LayoutParams) mDotsPageIndicator.getLayoutParams();
+ params.bottomMargin = mInsets.bottom;
+ mDotsPageIndicator.setLayoutParams(params);
+ }
+
+ mAdapter.setInsets(mInsets);
+ return insets;
+ }
+ });
+
+ // Set up the DismissOverlayView
+ mDismissOverlayView = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
+ mDismissOverlayView.setIntroText(getString(R.string.exit_intro_text));
+ mDismissOverlayView.showIntroIfNecessary();
+ mGestureDetector = new GestureDetectorCompat(this, new LongPressListener());
+
+ Uri attractionsUri = getIntent().getParcelableExtra(Constants.EXTRA_ATTRACTIONS_URI);
+ if (attractionsUri != null) {
+ new FetchDataAsyncTask(this).execute(attractionsUri);
+ UtilityService.clearNotification(this);
+ UtilityService.clearRemoteNotifications(this);
+ } else {
+ finish();
+ }
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+
+ @Override
+ public void onChromeFadeIn() {
+ // As the custom UI chrome fades in, also fade the DotsPageIndicator in
+ mDotsPageIndicator.animate().alpha(1).setDuration(
+ AttractionsGridPagerAdapter.FADE_IN_TIME_MS).start();
+ }
+
+ @Override
+ public void onChromeFadeOut() {
+ // As the custom UI chrome fades out, also fade the DotsPageIndicator out
+ mDotsPageIndicator.animate().alpha(0).setDuration(
+ AttractionsGridPagerAdapter.FADE_OUT_TIME_MS).start();
+ }
+
+ private class LongPressListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public void onLongPress(MotionEvent event) {
+ mDismissOverlayView.show();
+ }
+ }
+
+ /**
+ * A background task to load the attraction data via the Wear DataApi.
+ * This can take a second or two sometimes as several images need to
+ * be loaded.
+ */
+ private class FetchDataAsyncTask extends
+ AsyncTask<Uri, Void, ArrayList<Attraction>> {
+
+ private Context mContext;
+
+ public FetchDataAsyncTask(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected ArrayList<Attraction> doInBackground(Uri... params) {
+ mAttractions.clear();
+
+ // Connect to Play Services and the Wearable API
+ GoogleApiClient googleApiClient = new GoogleApiClient.Builder(mContext)
+ .addApi(Wearable.API)
+ .build();
+
+ ConnectionResult connectionResult = googleApiClient.blockingConnect(
+ Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
+
+ if (!connectionResult.isSuccess() || !googleApiClient.isConnected()) {
+ Log.e(TAG, String.format(Constants.GOOGLE_API_CLIENT_ERROR_MSG,
+ connectionResult.getErrorCode()));
+ return null;
+ }
+
+ Uri attractionsUri = params[0];
+ DataApi.DataItemResult dataItemResult =
+ Wearable.DataApi.getDataItem(googleApiClient, attractionsUri).await();
+
+ if (dataItemResult.getStatus().isSuccess() && dataItemResult.getDataItem() != null) {
+ DataMapItem dataMapItem = DataMapItem.fromDataItem(dataItemResult.getDataItem());
+ List<DataMap> attractionsData =
+ dataMapItem.getDataMap().getDataMapArrayList(Constants.EXTRA_ATTRACTIONS);
+
+ // Loop through each attraction, adding them to the list
+ Iterator<DataMap> itr = attractionsData.iterator();
+ while (itr.hasNext()) {
+ DataMap attractionData = itr.next();
+
+ Attraction attraction = new Attraction();
+ attraction.name = attractionData.getString(Constants.EXTRA_TITLE);
+ attraction.description =
+ attractionData.getString(Constants.EXTRA_DESCRIPTION);
+ attraction.city = attractionData.get(Constants.EXTRA_CITY);
+ attraction.distance =
+ attractionData.getString(Constants.EXTRA_DISTANCE);
+ attraction.location = new LatLng(
+ attractionData.getDouble(Constants.EXTRA_LOCATION_LAT),
+ attractionData.getDouble(Constants.EXTRA_LOCATION_LNG));
+ attraction.image = Utils.loadBitmapFromAsset(googleApiClient,
+ attractionData.getAsset(Constants.EXTRA_IMAGE));
+ attraction.secondaryImage = Utils.loadBitmapFromAsset(googleApiClient,
+ attractionData.getAsset(Constants.EXTRA_IMAGE_SECONDARY));
+
+ mAttractions.add(attraction);
+ }
+ }
+
+ googleApiClient.disconnect();
+
+ return mAttractions;
+ }
+
+ @Override
+ protected void onPostExecute(ArrayList<Attraction> result) {
+ if (result != null && result.size() > 0) {
+ // 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);
+ } else {
+ finish();
+ }
+ }
+ }
+}
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
new file mode 100644
index 0000000..99737f4
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Wearable/src/com.example.android.xyztouristattractions/ui/AttractionsGridPagerAdapter.java
@@ -0,0 +1,342 @@
+/*
+ * 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.ui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+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.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;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.example.android.xyztouristattractions.R;
+import com.example.android.xyztouristattractions.common.Attraction;
+import com.example.android.xyztouristattractions.common.Constants;
+import com.example.android.xyztouristattractions.service.UtilityService;
+
+import java.util.ArrayList;
+
+/**
+ * This adapter backs the main GridViewPager component found in
+ * {@link com.example.android.xyztouristattractions.ui.AttractionsActivity}.
+ */
+public class AttractionsGridPagerAdapter extends GridPagerAdapter
+ implements GridViewPager.OnPageChangeListener {
+
+ public static final int FADE_IN_TIME_MS = 250;
+ public static final int FADE_OUT_TIME_MS = 500;
+ private static final int GRID_COLUMN_COUNT = 5;
+ private static final int FADE_OUT_DELAY_MS = 1500;
+ private static final int PAGER_PRIMARY_IMAGE_COLUMN = 0;
+ private static final int PAGER_SECONDARY_IMAGE_COLUMN = 1;
+ private static final int PAGER_DESCRIPTION_COLUMN = 2;
+ private static final int PAGER_NAVIGATE_ACTION_COLUMN = 3;
+ private static final int PAGER_OPEN_ACTION_COLUMN = 4;
+
+ private Context mContext;
+ private LayoutInflater mLayoutInflater;
+ private ArrayList<Attraction> mAttractions;
+ private Rect mInsets = new Rect();
+ private DelayedHide mDelayedHide = new DelayedHide();
+ private OnChromeFadeListener mOnChromeFadeListener;
+
+ public AttractionsGridPagerAdapter(
+ Context context, ArrayList<Attraction> attractions) {
+ super();
+ mContext = context;
+ mLayoutInflater = LayoutInflater.from(context);
+ mAttractions = attractions;
+ }
+
+ public void setData(ArrayList<Attraction> attractions) {
+ mAttractions = attractions;
+ }
+
+ public void setInsets(Rect insets) {
+ mInsets = insets;
+ }
+
+ @Override
+ public int getRowCount() {
+ return (mAttractions != null && mAttractions.size() > 0) ? mAttractions.size() : 1;
+ }
+
+ @Override
+ public int getColumnCount(int i) {
+ return GRID_COLUMN_COUNT;
+ }
+
+ @Override
+ protected Object instantiateItem(ViewGroup container, int row, final int column) {
+ if (mAttractions != null && mAttractions.size() > 0) {
+ final Attraction attraction = mAttractions.get(row);
+ switch (column) {
+ case PAGER_PRIMARY_IMAGE_COLUMN:
+ case PAGER_SECONDARY_IMAGE_COLUMN:
+ // Two pages of full screen images, one with the attraction name
+ // and one with the distance to the attraction
+ final View view = mLayoutInflater.inflate(
+ R.layout.gridpager_fullscreen_image, container, false);
+ ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
+ TextView textView = (TextView) view.findViewById(R.id.textView);
+ FrameLayout overlayTextLayout =
+ (FrameLayout) view.findViewById(R.id.overlaytext);
+
+ mDelayedHide.add(overlayTextLayout);
+ view.setOnClickListener(mDelayedHide);
+
+ FrameLayout.LayoutParams params =
+ (FrameLayout.LayoutParams) textView.getLayoutParams();
+ params.bottomMargin = params.bottomMargin + mInsets.bottom;
+ params.leftMargin = mInsets.left;
+ params.rightMargin = mInsets.right;
+ textView.setLayoutParams(params);
+
+ if (column == PAGER_PRIMARY_IMAGE_COLUMN) {
+ imageView.setImageBitmap(attraction.image);
+ textView.setText(attraction.name);
+ } else {
+ imageView.setImageBitmap(attraction.secondaryImage);
+ if (TextUtils.isEmpty(attraction.distance)) {
+ overlayTextLayout.setVisibility(View.GONE);
+ } else {
+ textView.setText(mContext.getString(
+ R.string.map_caption, attraction.distance));
+ }
+ }
+ container.addView(view);
+ return view;
+ case PAGER_DESCRIPTION_COLUMN:
+ // The description card page
+ CardScrollView cardScrollView = (CardScrollView) mLayoutInflater.inflate(
+ R.layout.gridpager_card, container, false);
+ TextView descTextView = (TextView) cardScrollView.findViewById(R.id.textView);
+ descTextView.setText(attraction.description);
+ cardScrollView.setCardGravity(Gravity.BOTTOM);
+ cardScrollView.setExpansionEnabled(true);
+ cardScrollView.setExpansionDirection(CardFrame.EXPAND_DOWN);
+ cardScrollView.setExpansionFactor(10);
+ container.addView(cardScrollView);
+ return cardScrollView;
+ case PAGER_NAVIGATE_ACTION_COLUMN:
+ // The navigate action
+ final WatchViewStub navStub = (WatchViewStub) mLayoutInflater.inflate(
+ R.layout.gridpager_action, container, false);
+
+ navStub.setOnClickListener(getStartActionClickListener(
+ attraction, Constants.START_NAVIGATION_PATH,
+ ConfirmationActivity.SUCCESS_ANIMATION));
+
+ 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;
+ case PAGER_OPEN_ACTION_COLUMN:
+ // The "open on device" action
+ final WatchViewStub openStub = (WatchViewStub) mLayoutInflater.inflate(
+ R.layout.gridpager_action, container, false);
+
+ openStub.setOnClickListener(getStartActionClickListener(
+ attraction, Constants.START_ATTRACTION_PATH,
+ ConfirmationActivity.OPEN_ON_PHONE_ANIMATION));
+
+ 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;
+ }
+ }
+ return new View(mContext);
+ }
+
+ @Override
+ public Drawable getBackgroundForPage(int row, int column) {
+ if (column == 0) {
+ return new ColorDrawable(0); // Empty black drawable
+ }
+ if (mAttractions.size() > 0 && mAttractions.get(row).image != null) {
+ return new BitmapDrawable(mContext.getResources(), mAttractions.get(row).image);
+ }
+ return super.getBackgroundForPage(row, column);
+ }
+
+ @Override
+ protected void destroyItem(ViewGroup viewGroup, int row, int column, Object object) {
+ mDelayedHide.remove((View) object);
+ viewGroup.removeView((View)object);
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ @Override
+ public void onPageScrolled(int posX, int posY, float posOffsetX, float posOffsetY,
+ int posOffsetPixelsX, int posOffsetPixelsY) {}
+
+ @Override
+ public void onPageSelected(int row, int col) {}
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mDelayedHide.show();
+ }
+
+ /**
+ * Use the Wear Message API to execute an action. Clears local and remote notifications and
+ * also runs a confirmation animation before finishing the Wear activity.
+ *
+ * @param attraction The attraction to start the action on
+ * @param pathName The Wear Message API pathname
+ * @param confirmAnimationType The confirmation animation type from ConfirmationActivity
+ */
+ private void startAction(Attraction attraction, String pathName, int confirmAnimationType) {
+ Intent intent = new Intent(mContext, ConfirmationActivity.class);
+ intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE, confirmAnimationType);
+ mContext.startActivity(intent);
+
+ UtilityService.clearNotification(mContext);
+ UtilityService.clearRemoteNotifications(mContext);
+ UtilityService.startDeviceActivity(mContext, pathName, attraction.name, attraction.city);
+
+ ((Activity)mContext).finish();
+ }
+
+ /**
+ * Helper method to generate the OnClickListener for the attraction actions.
+ */
+ private View.OnClickListener getStartActionClickListener(final Attraction attraction,
+ final String pathName, final int confirmAnimationType) {
+ View.OnClickListener clickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startAction(attraction, pathName, confirmAnimationType);
+ }
+ };
+ return clickListener;
+ }
+
+ public void setOnChromeFadeListener(OnChromeFadeListener listener) {
+ mOnChromeFadeListener = listener;
+ }
+
+ public interface OnChromeFadeListener {
+ abstract void onChromeFadeIn();
+ abstract void onChromeFadeOut();
+ }
+
+ /**
+ * Helper class to fade out views based on a delay and fade them back in if needed as well.
+ */
+ private class DelayedHide implements View.OnClickListener {
+
+ ArrayList<View> hideViews = new ArrayList<View>(GRID_COLUMN_COUNT);
+ Handler mHideHandler = new Handler();
+ boolean mIsHidden = false;
+
+ Runnable mHideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ hide();
+ }
+ };
+
+ void add(View newView) {
+ hideViews.add(newView);
+ delayedHide();
+ }
+
+ void remove(View removeView) {
+ hideViews.remove(removeView);
+ }
+
+ void show() {
+ mIsHidden = false;
+ if (mOnChromeFadeListener != null) {
+ mOnChromeFadeListener.onChromeFadeIn();
+ }
+ for (View view : hideViews) {
+ if (view != null) {
+ view.animate().alpha(1).setDuration(FADE_IN_TIME_MS).start();
+ }
+ }
+ delayedHide();
+ }
+
+ void hide() {
+ mIsHidden = true;
+ mHideHandler.removeCallbacks(mHideRunnable);
+ if (mOnChromeFadeListener != null) {
+ mOnChromeFadeListener.onChromeFadeOut();
+ }
+ for (int i=0; i<hideViews.size(); i++) {
+ if (hideViews.get(i) != null) {
+ hideViews.get(i).animate().alpha(0).setDuration(FADE_OUT_TIME_MS).start();
+ }
+ }
+ }
+
+ void delayedHide() {
+ mHideHandler.removeCallbacks(mHideRunnable);
+ mHideHandler.postDelayed(mHideRunnable, FADE_OUT_DELAY_MS);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mIsHidden) {
+ show();
+ } else {
+ hide();
+ }
+ }
+ }
+}
diff --git a/samples/browseable/XYZTouristAttractions/_index.jd b/samples/browseable/XYZTouristAttractions/_index.jd
new file mode 100644
index 0000000..a48e947
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/_index.jd
@@ -0,0 +1,20 @@
+page.tags="XYZTouristAttractions"
+sample.group=Wearable
+@jd:body
+
+<p>
+
+This sample aims to be as close to a real world example of a mobile
+and Wear app combination as possible. It has a more refined design
+and also provides a practical example of how a mobile app would
+interact and communicate with its wear counterpart.
+
+The app itself is modeled after a hypothetical tourist attractions
+app that notifies the user when they are in close proximity to
+notable points of interest.
+
+The Wear component loads a full wearable app that shows images,
+summary information and provides quick actions for nearby tourist
+attractions in a GridViewPager UI component.
+
+ </p>