diff --git a/samples/browseable/DataLayer/Application/AndroidManifest.xml b/samples/browseable/DataLayer/Application/AndroidManifest.xml
index 6f7e81e8a..e80846de1 100644
--- a/samples/browseable/DataLayer/Application/AndroidManifest.xml
+++ b/samples/browseable/DataLayer/Application/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.wearable.datalayer" >
+ android:targetSdkVersion="22" />
diff --git a/samples/browseable/DataLayer/Application/res/values/wear.xml b/samples/browseable/DataLayer/Application/res/values/wear.xml
new file mode 100644
index 000000000..3220362d2
--- /dev/null
+++ b/samples/browseable/DataLayer/Application/res/values/wear.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ - capability_1
+ - capability_2
+
+
diff --git a/samples/browseable/DataLayer/Application/src/com.example.android.wearable.datalayer/MainActivity.java b/samples/browseable/DataLayer/Application/src/com.example.android.wearable.datalayer/MainActivity.java
index 23bc696d3..e6e4aa2cf 100644
--- a/samples/browseable/DataLayer/Application/src/com.example.android.wearable.datalayer/MainActivity.java
+++ b/samples/browseable/DataLayer/Application/src/com.example.android.wearable.datalayer/MainActivity.java
@@ -37,18 +37,18 @@ import android.widget.ListView;
import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.data.FreezableUtils;
import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.DataApi.DataItemResult;
import com.google.android.gms.wearable.DataEvent;
import com.google.android.gms.wearable.DataEventBuffer;
-import com.google.android.gms.wearable.MessageApi.SendMessageResult;
-import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.MessageApi;
+import com.google.android.gms.wearable.MessageApi.SendMessageResult;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
@@ -69,8 +69,8 @@ import java.util.concurrent.TimeUnit;
/**
* Receives its own events using a listener API designed for foreground activities. Updates a data
- * item every second while it is open. Also allows user to take a photo and send that as an asset to
- * the paired wearable.
+ * item every second while it is open. Also allows user to take a photo and send that as an asset
+ * to the paired wearable.
*/
public class MainActivity extends Activity implements DataApi.DataListener,
MessageApi.MessageListener, NodeApi.NodeListener, ConnectionCallbacks,
@@ -78,7 +78,9 @@ public class MainActivity extends Activity implements DataApi.DataListener,
private static final String TAG = "MainActivity";
- /** Request code for launching the Intent to resolve Google Play services errors. */
+ /**
+ * Request code for launching the Intent to resolve Google Play services errors.
+ */
private static final int REQUEST_RESOLVE_ERROR = 1000;
private static final String START_ACTIVITY_PATH = "/start-activity";
@@ -92,7 +94,6 @@ public class MainActivity extends Activity implements DataApi.DataListener,
private boolean mCameraSupported = false;
private ListView mDataItemList;
- private Button mTakePhotoBtn;
private Button mSendPhotoBtn;
private ImageView mThumbView;
private Bitmap mImageBitmap;
@@ -320,7 +321,7 @@ public class MainActivity extends Activity implements DataApi.DataListener,
}
private Collection getNodes() {
- HashSet results = new HashSet();
+ HashSet results = new HashSet<>();
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
@@ -358,7 +359,9 @@ public class MainActivity extends Activity implements DataApi.DataListener,
}
}
- /** Sends an RPC to start a fullscreen Activity on the wearable. */
+ /**
+ * Sends an RPC to start a fullscreen Activity on the wearable.
+ */
public void onStartWearableActivityClick(View view) {
LOGD(TAG, "Generating RPC");
@@ -367,7 +370,9 @@ public class MainActivity extends Activity implements DataApi.DataListener,
new StartWearableActivityTask().execute();
}
- /** Generates a DataItem based on an incrementing count. */
+ /**
+ * Generates a DataItem based on an incrementing count.
+ */
private class DataItemGenerator implements Runnable {
private int count = 0;
@@ -462,13 +467,9 @@ public class MainActivity extends Activity implements DataApi.DataListener,
* Sets up UI components and their callback handlers.
*/
private void setupViews() {
- mTakePhotoBtn = (Button) findViewById(R.id.takePhoto);
mSendPhotoBtn = (Button) findViewById(R.id.sendPhoto);
-
- // Shows the image received from the handset
mThumbView = (ImageView) findViewById(R.id.imageView);
mDataItemList = (ListView) findViewById(R.id.data_item_list);
-
mStartActivityBtn = findViewById(R.id.start_wearable_activity);
}
diff --git a/samples/browseable/DataLayer/Wearable/AndroidManifest.xml b/samples/browseable/DataLayer/Wearable/AndroidManifest.xml
index 4cc903409..5567365db 100644
--- a/samples/browseable/DataLayer/Wearable/AndroidManifest.xml
+++ b/samples/browseable/DataLayer/Wearable/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.wearable.datalayer" >
+ android:targetSdkVersion="22" />
diff --git a/samples/browseable/DataLayer/Wearable/res/drawable-nodpi/photo_placeholder.png b/samples/browseable/DataLayer/Wearable/res/drawable-nodpi/photo_placeholder.png
new file mode 100644
index 000000000..d794731d4
Binary files /dev/null and b/samples/browseable/DataLayer/Wearable/res/drawable-nodpi/photo_placeholder.png differ
diff --git a/samples/browseable/DataLayer/Wearable/res/drawable/rounded_background.xml b/samples/browseable/DataLayer/Wearable/res/drawable/rounded_background.xml
new file mode 100644
index 000000000..226c174df
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/res/drawable/rounded_background.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/samples/browseable/DataLayer/Wearable/res/layout/asset_fragment.xml b/samples/browseable/DataLayer/Wearable/res/layout/asset_fragment.xml
new file mode 100644
index 000000000..3c5f7b107
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/res/layout/asset_fragment.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/browseable/DataLayer/Wearable/res/layout/data_fragment.xml b/samples/browseable/DataLayer/Wearable/res/layout/data_fragment.xml
new file mode 100644
index 000000000..efbbe9c48
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/res/layout/data_fragment.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/samples/browseable/DataLayer/Wearable/res/layout/discovery_fragment.xml b/samples/browseable/DataLayer/Wearable/res/layout/discovery_fragment.xml
new file mode 100644
index 000000000..5cd247cff
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/res/layout/discovery_fragment.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/browseable/DataLayer/Wearable/res/layout/main_activity.xml b/samples/browseable/DataLayer/Wearable/res/layout/main_activity.xml
index f98e6672a..4608e4bcb 100644
--- a/samples/browseable/DataLayer/Wearable/res/layout/main_activity.xml
+++ b/samples/browseable/DataLayer/Wearable/res/layout/main_activity.xml
@@ -15,25 +15,24 @@
-->
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/white">
-
+ android:layout_height="match_parent" />
-
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="10dp"
+ android:padding="5dp"
+ android:background="@drawable/rounded_background">
+
+
diff --git a/samples/browseable/DataLayer/Wearable/res/values/dimens.xml b/samples/browseable/DataLayer/Wearable/res/values/dimens.xml
new file mode 100644
index 000000000..f24f84dca
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/res/values/dimens.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ 15dp
+
\ No newline at end of file
diff --git a/samples/browseable/DataLayer/Wearable/res/values/strings.xml b/samples/browseable/DataLayer/Wearable/res/values/strings.xml
index 331e73604..3dc0a88a8 100644
--- a/samples/browseable/DataLayer/Wearable/res/values/strings.xml
+++ b/samples/browseable/DataLayer/Wearable/res/values/strings.xml
@@ -17,4 +17,8 @@
Data Layer Sample Wearable App
Waiting for data to arrive\u2026
+ Capability 2
+ Capabilities 1 and 2
+ No device provides the requested capabilities
+ Nodes: %1$s
diff --git a/samples/browseable/DataLayer/Wearable/res/values/wear.xml b/samples/browseable/DataLayer/Wearable/res/values/wear.xml
new file mode 100644
index 000000000..41dc3bbec
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/res/values/wear.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ - capability_1
+
+
diff --git a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/DataLayerListenerService.java b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/DataLayerListenerService.java
index 64abaf54d..34d3ca390 100644
--- a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/DataLayerListenerService.java
+++ b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/DataLayerListenerService.java
@@ -45,8 +45,6 @@ public class DataLayerListenerService extends WearableListenerService {
public static final String COUNT_PATH = "/count";
public static final String IMAGE_PATH = "/image";
public static final String IMAGE_KEY = "photo";
- private static final String COUNT_KEY = "count";
- private static final int MAX_LOG_TAG_LENGTH = 23;
GoogleApiClient mGoogleApiClient;
@Override
@@ -63,11 +61,12 @@ public class DataLayerListenerService extends WearableListenerService {
LOGD(TAG, "onDataChanged: " + dataEvents);
final List events = FreezableUtils.freezeIterable(dataEvents);
dataEvents.close();
- if(!mGoogleApiClient.isConnected()) {
+ if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting()) {
ConnectionResult connectionResult = mGoogleApiClient
.blockingConnect(30, TimeUnit.SECONDS);
if (!connectionResult.isSuccess()) {
- Log.e(TAG, "DataLayerListenerService failed to connect to GoogleApiClient.");
+ Log.e(TAG, "DataLayerListenerService failed to connect to GoogleApiClient, "
+ + "error code: " + connectionResult.getErrorCode());
return;
}
}
diff --git a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java
index b3121352e..7d2a28c8c 100644
--- a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java
+++ b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java
@@ -19,27 +19,33 @@ package com.example.android.wearable.datalayer;
import static com.example.android.wearable.datalayer.DataLayerListenerService.LOGD;
import android.app.Activity;
-import android.content.Context;
+import android.app.Fragment;
+import android.app.FragmentManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Handler;
+import android.support.wearable.view.DotsPageIndicator;
+import android.support.wearable.view.FragmentGridPagerAdapter;
+import android.support.wearable.view.GridViewPager;
+import android.text.TextUtils;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
+import android.widget.Toast;
+import com.example.android.wearable.datalayer.fragments.AssetFragment;
+import com.example.android.wearable.datalayer.fragments.DataFragment;
+import com.example.android.wearable.datalayer.fragments.DiscoveryFragment;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
+import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.data.FreezableUtils;
import com.google.android.gms.wearable.Asset;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
import com.google.android.gms.wearable.DataApi;
import com.google.android.gms.wearable.DataEvent;
import com.google.android.gms.wearable.DataEventBuffer;
@@ -51,39 +57,47 @@ import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
- * Shows events and photo from the Wearable APIs.
+ * The main activity with a view pager, containing three pages:
+ *
+ * -
+ * Page 1: shows a list of DataItems received from the phone application
+ *
+ * -
+ * Page 2: shows the photo that is sent from the phone application
+ *
+ * -
+ * Page 3: includes two buttons to show the connected phone and watch devices
+ *
+ *
*/
public class MainActivity extends Activity implements ConnectionCallbacks,
OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener,
NodeApi.NodeListener {
private static final String TAG = "MainActivity";
+ private static final String CAPABILITY_1_NAME = "capability_1";
+ private static final String CAPABILITY_2_NAME = "capability_2";
private GoogleApiClient mGoogleApiClient;
- private ListView mDataItemList;
- private TextView mIntroText;
- private DataItemAdapter mDataItemListAdapter;
- private View mLayout;
private Handler mHandler;
+ private GridViewPager mPager;
+ private DataFragment mDataFragment;
+ private AssetFragment mAssetFragment;
@Override
public void onCreate(Bundle b) {
super.onCreate(b);
mHandler = new Handler();
- LOGD(TAG, "onCreate");
setContentView(R.layout.main_activity);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- mDataItemList = (ListView) findViewById(R.id.dataItem_list);
- mIntroText = (TextView) findViewById(R.id.intro);
- mLayout = findViewById(R.id.layout);
-
- // Stores data events received by the local broadcaster.
- mDataItemListAdapter = new DataItemAdapter(this, android.R.layout.simple_list_item_1);
- mDataItemList.setAdapter(mDataItemListAdapter);
-
+ setupViews();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
@@ -128,8 +142,7 @@ public class MainActivity extends Activity implements ConnectionCallbacks,
runOnUiThread(new Runnable() {
@Override
public void run() {
- mIntroText.setVisibility(View.INVISIBLE);
- mDataItemListAdapter.add(new Event(title, text));
+ mDataFragment.appendItem(title, text);
}
});
}
@@ -151,8 +164,9 @@ public class MainActivity extends Activity implements ConnectionCallbacks,
mHandler.post(new Runnable() {
@Override
public void run() {
- Log.d(TAG, "Setting background image..");
- mLayout.setBackground(new BitmapDrawable(getResources(), bitmap));
+ Log.d(TAG, "Setting background image on second page..");
+ moveToPage(1);
+ mAssetFragment.setBackgroundImage(bitmap);
}
});
@@ -171,6 +185,70 @@ public class MainActivity extends Activity implements ConnectionCallbacks,
}
}
+ public void onClicked(View view) {
+ switch (view.getId()) {
+ case R.id.capability_2_btn:
+ showNodes(CAPABILITY_2_NAME);
+ break;
+ case R.id.capabilities_1_and_2_btn:
+ showNodes(CAPABILITY_1_NAME, CAPABILITY_2_NAME);
+ break;
+ default:
+ Log.e(TAG, "Unknown click event registered");
+ }
+ }
+
+ /**
+ * Find the connected nodes that provide at least one of the given capabilities
+ */
+ private void showNodes(final String... capabilityNames) {
+ Wearable.CapabilityApi.getAllCapabilities(mGoogleApiClient,
+ CapabilityApi.FILTER_REACHABLE).setResultCallback(
+
+ new ResultCallback() {
+ @Override
+ public void onResult(
+ CapabilityApi.GetAllCapabilitiesResult getAllCapabilitiesResult) {
+ if (!getAllCapabilitiesResult.getStatus().isSuccess()) {
+ Log.e(TAG, "Failed to get capabilities");
+ return;
+ }
+ Map
+ capabilitiesMap = getAllCapabilitiesResult.getAllCapabilities();
+ Set nodes = new HashSet<>();
+ if (capabilitiesMap.isEmpty()) {
+ showDiscoveredNodes(nodes);
+ return;
+ }
+ for (String capabilityName : capabilityNames) {
+ CapabilityInfo capabilityInfo = capabilitiesMap.get(capabilityName);
+ if (capabilityInfo != null) {
+ nodes.addAll(capabilityInfo.getNodes());
+ }
+ }
+ showDiscoveredNodes(nodes);
+ }
+
+ private void showDiscoveredNodes(Set nodes) {
+ List nodesList = new ArrayList<>();
+ for (Node node : nodes) {
+ nodesList.add(node.getDisplayName());
+ }
+ Log.d(TAG, "Connected Nodes: " + (nodesList.isEmpty()
+ ? "No connected device was found for the given capabilities"
+ : TextUtils.join(",", nodesList)));
+ String msg;
+ if (!nodesList.isEmpty()) {
+ msg = getString(R.string.connected_nodes,
+ TextUtils.join(", ", nodesList));
+ } else {
+ msg = getString(R.string.no_device);
+ }
+ Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+
/**
* Extracts {@link android.graphics.Bitmap} data from the
* {@link com.google.android.gms.wearable.Asset}
@@ -206,50 +284,53 @@ public class MainActivity extends Activity implements ConnectionCallbacks,
generateEvent("Node Disconnected", node.getId());
}
- private static class DataItemAdapter extends ArrayAdapter {
+ private void setupViews() {
+ mPager = (GridViewPager) findViewById(R.id.pager);
+ mPager.setOffscreenPageCount(2);
+ DotsPageIndicator dotsPageIndicator = (DotsPageIndicator) findViewById(R.id.page_indicator);
+ dotsPageIndicator.setDotSpacing((int) getResources().getDimension(R.dimen.dots_spacing));
+ dotsPageIndicator.setPager(mPager);
+ mDataFragment = new DataFragment();
+ mAssetFragment = new AssetFragment();
+ DiscoveryFragment discoveryFragment = new DiscoveryFragment();
+ List pages = new ArrayList<>();
+ pages.add(mDataFragment);
+ pages.add(mAssetFragment);
+ pages.add(discoveryFragment);
+ final MyPagerAdapter adapter = new MyPagerAdapter(getFragmentManager(), pages);
+ mPager.setAdapter(adapter);
+ }
- private final Context mContext;
+ /**
+ * Switches to the page {@code index}. The first page has index 0.
+ */
+ private void moveToPage(int index) {
+ mPager.setCurrentItem(0, index, true);
+ }
- public DataItemAdapter(Context context, int unusedResource) {
- super(context, unusedResource);
- mContext = context;
+ private class MyPagerAdapter extends FragmentGridPagerAdapter {
+
+ private List mFragments;
+
+ public MyPagerAdapter(FragmentManager fm, List fragments) {
+ super(fm);
+ mFragments = fragments;
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder;
- if (convertView == null) {
- holder = new ViewHolder();
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(android.R.layout.two_line_list_item, null);
- convertView.setTag(holder);
- holder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
- holder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
- Event event = getItem(position);
- holder.text1.setText(event.title);
- holder.text2.setText(event.text);
- return convertView;
+ public int getRowCount() {
+ return 1;
}
- private class ViewHolder {
-
- TextView text1;
- TextView text2;
+ @Override
+ public int getColumnCount(int row) {
+ return mFragments == null ? 0 : mFragments.size();
}
- }
- private class Event {
-
- String title;
- String text;
-
- public Event(String title, String text) {
- this.title = title;
- this.text = text;
+ @Override
+ public Fragment getFragment(int row, int column) {
+ return mFragments.get(column);
}
+
}
}
diff --git a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/AssetFragment.java b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/AssetFragment.java
new file mode 100644
index 000000000..879c2a7bd
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/AssetFragment.java
@@ -0,0 +1,49 @@
+/*
+ * 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.wearable.datalayer.fragments;
+
+import android.app.Fragment;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.example.android.wearable.datalayer.R;
+
+/**
+ * A simple fragment that shows a (photo) asset received from the phone.
+ */
+public class AssetFragment extends Fragment {
+
+ private ImageView mPhoto;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.asset_fragment, container, false);
+ mPhoto = (ImageView) view.findViewById(R.id.photo);
+ return view;
+ }
+
+ public void setBackgroundImage(Bitmap bitmap) {
+ mPhoto.setImageBitmap(bitmap);
+ }
+}
diff --git a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/DataFragment.java b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/DataFragment.java
new file mode 100644
index 000000000..f111e7d4c
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/DataFragment.java
@@ -0,0 +1,109 @@
+/*
+ * 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.wearable.datalayer.fragments;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.example.android.wearable.datalayer.R;
+
+/**
+ * A fragment that shows a list of DataItems received from the phone
+ */
+public class DataFragment extends Fragment {
+
+ private DataItemAdapter mDataItemListAdapter;
+ private TextView mIntroText;
+ private boolean mInitialized;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.data_fragment, container, false);
+ ListView dataItemList = (ListView) view.findViewById(R.id.dataItem_list);
+ mIntroText = (TextView) view.findViewById(R.id.intro);
+ mDataItemListAdapter = new DataItemAdapter(getActivity(),
+ android.R.layout.simple_list_item_1);
+ dataItemList.setAdapter(mDataItemListAdapter);
+ mInitialized = true;
+ return view;
+ }
+
+ public void appendItem(String title, String text) {
+ if (!mInitialized) {
+ return;
+ }
+ mIntroText.setVisibility(View.INVISIBLE);
+ mDataItemListAdapter.add(new Event(title, text));
+ }
+
+ private static class DataItemAdapter extends ArrayAdapter {
+
+ private final Context mContext;
+
+ public DataItemAdapter(Context context, int unusedResource) {
+ super(context, unusedResource);
+ mContext = context;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+ if (convertView == null) {
+ holder = new ViewHolder();
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ convertView = inflater.inflate(android.R.layout.two_line_list_item, null);
+ convertView.setTag(holder);
+ holder.text1 = (TextView) convertView.findViewById(android.R.id.text1);
+ holder.text2 = (TextView) convertView.findViewById(android.R.id.text2);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ Event event = getItem(position);
+ holder.text1.setText(event.title);
+ holder.text2.setText(event.text);
+ return convertView;
+ }
+
+ private class ViewHolder {
+
+ TextView text1;
+ TextView text2;
+ }
+ }
+
+ private class Event {
+
+ String title;
+ String text;
+
+ public Event(String title, String text) {
+ this.title = title;
+ this.text = text;
+ }
+ }
+}
diff --git a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/DiscoveryFragment.java b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/DiscoveryFragment.java
new file mode 100644
index 000000000..90b4b0651
--- /dev/null
+++ b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/fragments/DiscoveryFragment.java
@@ -0,0 +1,39 @@
+/*
+ * 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.wearable.datalayer.fragments;
+
+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;
+
+import com.example.android.wearable.datalayer.R;
+
+/**
+ * A simple fragment with two buttons to show connected phones and watches
+ */
+public class DiscoveryFragment extends Fragment {
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.discovery_fragment, container, false);
+ }
+}
diff --git a/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml b/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml
index c9fdccbf1..d8060a8d4 100644
--- a/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml
+++ b/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.wearable.delayedconfirmation" >
+ android:targetSdkVersion="22" />
+
+
+ - confirmation_handler
+
+
diff --git a/samples/browseable/DelayedConfirmation/Application/src/com.example.android.wearable.delayedconfirmation/MainActivity.java b/samples/browseable/DelayedConfirmation/Application/src/com.example.android.wearable.delayedconfirmation/MainActivity.java
index 3ffda5374..aaa72c373 100644
--- a/samples/browseable/DelayedConfirmation/Application/src/com.example.android.wearable.delayedconfirmation/MainActivity.java
+++ b/samples/browseable/DelayedConfirmation/Application/src/com.example.android.wearable.delayedconfirmation/MainActivity.java
@@ -109,15 +109,15 @@ public class MainActivity extends Activity implements MessageApi.MessageListener
public void onStartWearableActivityClick(View view) {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
new ResultCallback() {
- @Override
- public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
- for (final Node node : getConnectedNodesResult.getNodes()) {
- Wearable.MessageApi.sendMessage(
- mGoogleApiClient, node.getId(), START_ACTIVITY_PATH, new byte[0])
- .setResultCallback(getSendMessageResultCallback());
- }
- }
- });
+ @Override
+ public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
+ for (final Node node : getConnectedNodesResult.getNodes()) {
+ Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(),
+ START_ACTIVITY_PATH, new byte[0]).setResultCallback(
+ getSendMessageResultCallback());
+ }
+ }
+ });
}
private ResultCallback getSendMessageResultCallback() {
diff --git a/samples/browseable/DelayedConfirmation/Wearable/AndroidManifest.xml b/samples/browseable/DelayedConfirmation/Wearable/AndroidManifest.xml
index 845c07048..99226f682 100644
--- a/samples/browseable/DelayedConfirmation/Wearable/AndroidManifest.xml
+++ b/samples/browseable/DelayedConfirmation/Wearable/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.wearable.delayedconfirmation" >
+ android:targetSdkVersion="22" />
diff --git a/samples/browseable/DelayedConfirmation/Wearable/res/values/strings.xml b/samples/browseable/DelayedConfirmation/Wearable/res/values/strings.xml
index 46f5326bc..d336a5cb2 100644
--- a/samples/browseable/DelayedConfirmation/Wearable/res/values/strings.xml
+++ b/samples/browseable/DelayedConfirmation/Wearable/res/values/strings.xml
@@ -22,4 +22,5 @@
DelayedConfirmation
Timer Selected
Timer Finished
+ No device able to confirm was found
diff --git a/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/MainActivity.java b/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/MainActivity.java
index f9b126db3..64acd83d8 100644
--- a/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/MainActivity.java
+++ b/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/MainActivity.java
@@ -23,19 +23,23 @@ import android.os.Bundle;
import android.support.wearable.view.DelayedConfirmationView;
import android.util.Log;
import android.view.View;
+import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
+import java.util.Set;
public class MainActivity extends Activity implements
DelayedConfirmationView.DelayedConfirmationListener,
- GoogleApiClient.OnConnectionFailedListener {
+ GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks,
+ CapabilityApi.CapabilityListener {
private static final String TAG = "DelayedConfirmation";
private static final int NUM_SECONDS = 5;
@@ -43,9 +47,15 @@ public class MainActivity extends Activity implements
private static final String TIMER_SELECTED_PATH = "/timer_selected";
private static final String TIMER_FINISHED_PATH = "/timer_finished";
+ /* name of the capability that the phone side provides */
+ private static final String CONFIRMATION_HANDLER_CAPABILITY_NAME = "confirmation_handler";
+
private DelayedConfirmationView delayedConfirmationView;
private GoogleApiClient mGoogleApiClient;
+ /* the preferred note that can handle the confirmation capability */
+ private Node mConfirmationHandlerNode;
+
@Override
public void onCreate(Bundle b) {
super.onCreate(b);
@@ -55,6 +65,7 @@ public class MainActivity extends Activity implements
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addOnConnectionFailedListener(this)
+ .addConnectionCallbacks(this)
.build();
}
@@ -67,11 +78,13 @@ public class MainActivity extends Activity implements
}
@Override
- protected void onDestroy() {
+ protected void onPause() {
if (mGoogleApiClient.isConnected()) {
+ Wearable.CapabilityApi.removeCapabilityListener(mGoogleApiClient, this,
+ CONFIRMATION_HANDLER_CAPABILITY_NAME);
mGoogleApiClient.disconnect();
}
- super.onDestroy();
+ super.onPause();
}
/**
@@ -112,32 +125,94 @@ public class MainActivity extends Activity implements
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.e(TAG, "Failed to connect to Google Api Client");
+ mConfirmationHandlerNode = null;
}
private void sendMessageToCompanion(final String path) {
- Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
- new ResultCallback() {
- @Override
- public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
- for (final Node node : getConnectedNodesResult.getNodes()) {
- Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path,
- new byte[0]).setResultCallback(getSendMessageResultCallback());
- }
- }
- }
- );
-
+ if (mConfirmationHandlerNode != null) {
+ Wearable.MessageApi.sendMessage(mGoogleApiClient, mConfirmationHandlerNode.getId(),
+ path, new byte[0])
+ .setResultCallback(getSendMessageResultCallback(mConfirmationHandlerNode));
+ } else {
+ Toast.makeText(this, R.string.no_device_found, Toast.LENGTH_SHORT).show();
+ }
}
- private ResultCallback getSendMessageResultCallback() {
+ private ResultCallback getSendMessageResultCallback(
+ final Node node) {
return new ResultCallback() {
@Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
- Log.e(TAG, "Failed to connect to Google Api Client with status "
+ Log.e(TAG, "Failed to send message with status "
+ sendMessageResult.getStatus());
+ } else {
+ Log.d(TAG, "Sent confirmation message to node " + node.getDisplayName());
}
}
};
}
+
+ private void setupConfirmationHandlerNode() {
+ Wearable.CapabilityApi.addCapabilityListener(
+ mGoogleApiClient, this, CONFIRMATION_HANDLER_CAPABILITY_NAME);
+
+ Wearable.CapabilityApi.getCapability(
+ mGoogleApiClient, CONFIRMATION_HANDLER_CAPABILITY_NAME,
+ CapabilityApi.FILTER_REACHABLE).setResultCallback(
+ new ResultCallback() {
+ @Override
+ public void onResult(CapabilityApi.GetCapabilityResult result) {
+ if (!result.getStatus().isSuccess()) {
+ Log.e(TAG, "setupConfirmationHandlerNode() Failed to get capabilities, "
+ + "status: " + result.getStatus().getStatusMessage());
+ return;
+ }
+ updateConfirmationCapability(result.getCapability());
+ }
+ });
+ }
+
+ private void updateConfirmationCapability(CapabilityInfo capabilityInfo) {
+ Set connectedNodes = capabilityInfo.getNodes();
+ if (connectedNodes.isEmpty()) {
+ mConfirmationHandlerNode = null;
+ } else {
+ mConfirmationHandlerNode = pickBestNode(connectedNodes);
+ }
+ }
+
+ /**
+ * We pick a node that is capabale of handling the confirmation. If there is more than one,
+ * then we would prefer the one that is directly connected to this device. In general,
+ * depending on the situation and requirements, the "best" node might be picked based on other
+ * criteria.
+ */
+ private Node pickBestNode(Set connectedNodes) {
+ Node best = null;
+ if (connectedNodes != null) {
+ for (Node node : connectedNodes) {
+ if (node.isNearby()) {
+ return node;
+ }
+ best = node;
+ }
+ }
+ return best;
+ }
+
+ @Override
+ public void onConnected(Bundle bundle) {
+ setupConfirmationHandlerNode();
+ }
+
+ @Override
+ public void onConnectionSuspended(int cause) {
+ mConfirmationHandlerNode = null;
+ }
+
+ @Override
+ public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
+ updateConfirmationCapability(capabilityInfo);
+ }
}
diff --git a/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/WearableMessageListenerService.java b/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/WearableMessageListenerService.java
index bb2ec9193..de854a511 100644
--- a/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/WearableMessageListenerService.java
+++ b/samples/browseable/DelayedConfirmation/Wearable/src/com.example.android.wearable.delayedconfirmation/WearableMessageListenerService.java
@@ -25,6 +25,7 @@ import com.google.android.gms.wearable.WearableListenerService;
* Listens for a message telling it to start the Wearable MainActivity.
*/
public class WearableMessageListenerService extends WearableListenerService {
+
private static final String START_ACTIVITY_PATH = "/start-activity";
@Override
diff --git a/samples/browseable/FindMyPhone/Application/AndroidManifest.xml b/samples/browseable/FindMyPhone/Application/AndroidManifest.xml
index 8cb70d188..af108af42 100644
--- a/samples/browseable/FindMyPhone/Application/AndroidManifest.xml
+++ b/samples/browseable/FindMyPhone/Application/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.wearable.findphone">
+ android:targetSdkVersion="22" />
+
+
+ - find_me
+
+
+
diff --git a/samples/browseable/FindMyPhone/Wearable/AndroidManifest.xml b/samples/browseable/FindMyPhone/Wearable/AndroidManifest.xml
index 18b5209d6..f72a1a0ab 100644
--- a/samples/browseable/FindMyPhone/Wearable/AndroidManifest.xml
+++ b/samples/browseable/FindMyPhone/Wearable/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.wearable.findphone" >
+ android:targetSdkVersion="22" />
diff --git a/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/DisconnectListenerService.java b/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/DisconnectListenerService.java
index 42237de3e..48cd90824 100644
--- a/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/DisconnectListenerService.java
+++ b/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/DisconnectListenerService.java
@@ -18,25 +18,105 @@ package com.example.android.wearable.findphone;
import android.app.Notification;
import android.app.NotificationManager;
+import android.os.Bundle;
+import android.util.Log;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.common.api.ResultCallback;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
+import com.google.android.gms.wearable.Node;
+import com.google.android.gms.wearable.Wearable;
import com.google.android.gms.wearable.WearableListenerService;
+import java.util.List;
+import java.util.Set;
+
/**
- * Listens for disconnection from home device.
+ * Listens for changes in connectivity between this wear device and the phone. More precisely, we
+ * need to distinguish the case that the wear device and the phone are connected directly from all
+ * other possible cases. To this end, the phone app has registered itself to provide the "find_me"
+ * capability and we need to look for connected nodes that provide this capability AND are nearby,
+ * to exclude a connection through the cloud. The proper way would have been to use the
+ * {@code onCapabilitiesChanged()} callback but currently that callback cannot discover the case
+ * where a connection switches from wifi to direct; this shortcoming will be addressed in future
+ * updates but for now we will use the {@code onConnectedNodes()} callback.
*/
-public class DisconnectListenerService extends WearableListenerService {
+public class DisconnectListenerService extends WearableListenerService
+ implements GoogleApiClient.ConnectionCallbacks {
private static final String TAG = "ExampleFindPhoneApp";
private static final int FORGOT_PHONE_NOTIFICATION_ID = 1;
+ /* the capability that the phone app would provide */
+ private static final String FIND_ME_CAPABILITY_NAME = "find_me";
+
+ private GoogleApiClient mGoogleApiClient;
+
@Override
- public void onPeerDisconnected(com.google.android.gms.wearable.Node peer) {
- // Create a "forgot phone" notification when phone connection is broken.
+ public void onCreate() {
+ super.onCreate();
+ mGoogleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .addConnectionCallbacks(this)
+ .build();
+ }
+
+ @Override
+ public void onConnectedNodes(List connectedNodes) {
+ // After we are notified by this callback, we need to query for the nodes that provide the
+ // "find_me" capability and are directly connected.
+ if (mGoogleApiClient.isConnected()) {
+ setOrUpdateNotification();
+ } else if (!mGoogleApiClient.isConnecting()) {
+ mGoogleApiClient.connect();
+ }
+ }
+
+ private void setOrUpdateNotification() {
+ Wearable.CapabilityApi.getCapability(
+ mGoogleApiClient, FIND_ME_CAPABILITY_NAME,
+ CapabilityApi.FILTER_REACHABLE).setResultCallback(
+ new ResultCallback() {
+ @Override
+ public void onResult(CapabilityApi.GetCapabilityResult result) {
+ if (result.getStatus().isSuccess()) {
+ updateFindMeCapability(result.getCapability());
+ } else {
+ Log.e(TAG,
+ "setOrUpdateNotification() Failed to get capabilities, "
+ + "status: "
+ + result.getStatus().getStatusMessage());
+ }
+ }
+ });
+ }
+
+ private void updateFindMeCapability(CapabilityInfo capabilityInfo) {
+ Set connectedNodes = capabilityInfo.getNodes();
+ if (connectedNodes.isEmpty()) {
+ setupLostConnectivityNotification();
+ } else {
+ for (Node node : connectedNodes) {
+ // we are only considering those nodes that are directly connected
+ if (node.isNearby()) {
+ ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
+ .cancel(FORGOT_PHONE_NOTIFICATION_ID);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a notification to inform user that the connectivity to phone has been lost (possibly
+ * left the phone behind).
+ */
+ private void setupLostConnectivityNotification() {
Notification.Builder notificationBuilder = new Notification.Builder(this)
.setContentTitle(getString(R.string.left_phone_title))
.setContentText(getString(R.string.left_phone_content))
- .setVibrate(new long[] {0, 200}) // Vibrate for 200 milliseconds.
+ .setVibrate(new long[]{0, 200}) // Vibrate for 200 milliseconds.
.setSmallIcon(R.drawable.ic_launcher)
.setLocalOnly(true)
.setPriority(Notification.PRIORITY_MAX);
@@ -46,10 +126,20 @@ public class DisconnectListenerService extends WearableListenerService {
}
@Override
- public void onPeerConnected(com.google.android.gms.wearable.Node peer) {
- // Remove the "forgot phone" notification when connection is restored.
- ((NotificationManager) getSystemService(NOTIFICATION_SERVICE))
- .cancel(FORGOT_PHONE_NOTIFICATION_ID);
+ public void onConnected(Bundle bundle) {
+ setOrUpdateNotification();
}
+ @Override
+ public void onConnectionSuspended(int cause) {
+
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting()) {
+ mGoogleApiClient.disconnect();
+ }
+ super.onDestroy();
+ }
}
diff --git a/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneActivity.java b/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneActivity.java
index d4eb18bf4..e42fb0af6 100644
--- a/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneActivity.java
+++ b/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneActivity.java
@@ -28,7 +28,6 @@ import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.RelativeSizeSpan;
-
public class FindPhoneActivity extends Activity {
private static final int FIND_PHONE_NOTIFICATION_ID = 2;
diff --git a/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneService.java b/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneService.java
index e5fa6c0ca..97204b475 100644
--- a/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneService.java
+++ b/samples/browseable/FindMyPhone/Wearable/src/com.example.android.wearable.findphone/FindPhoneService.java
@@ -79,8 +79,8 @@ public class FindPhoneService extends IntentService implements GoogleApiClient.C
.getBoolean(FIELD_ALARM_ON, false);
} else {
Log.e(TAG, "Unexpected number of DataItems found.\n"
- + "\tExpected: 1\n"
- + "\tActual: " + result.getCount());
+ + "\tExpected: 1\n"
+ + "\tActual: " + result.getCount());
}
} else if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onHandleIntent: failed to get current alarm state");
@@ -90,7 +90,7 @@ public class FindPhoneService extends IntentService implements GoogleApiClient.C
alarmOn = !alarmOn;
// Change notification text based on new value of alarmOn.
String notificationText = alarmOn ? getString(R.string.turn_alarm_off)
- : getString(R.string.turn_alarm_on);
+ : getString(R.string.turn_alarm_on);
FindPhoneActivity.updateNotification(this, notificationText);
}
// Use alarmOn boolean to update the DataItem - phone will respond accordingly
@@ -101,7 +101,7 @@ public class FindPhoneService extends IntentService implements GoogleApiClient.C
.await();
} else {
Log.e(TAG, "Failed to toggle alarm on phone - Client disconnected from Google Play "
- + "Services");
+ + "Services");
}
mGoogleApiClient.disconnect();
}
diff --git a/samples/browseable/Quiz/Application/AndroidManifest.xml b/samples/browseable/Quiz/Application/AndroidManifest.xml
index 55b666f14..801a47323 100644
--- a/samples/browseable/Quiz/Application/AndroidManifest.xml
+++ b/samples/browseable/Quiz/Application/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.example.android.wearable.quiz" >
+ android:targetSdkVersion="22" />
+
+
+ - reset_quiz
+
+
+
diff --git a/samples/browseable/Quiz/Application/src/com.example.android.wearable.quiz/MainActivity.java b/samples/browseable/Quiz/Application/src/com.example.android.wearable.quiz/MainActivity.java
index 0ff41f42d..1e589353c 100644
--- a/samples/browseable/Quiz/Application/src/com.example.android.wearable.quiz/MainActivity.java
+++ b/samples/browseable/Quiz/Application/src/com.example.android.wearable.quiz/MainActivity.java
@@ -210,6 +210,7 @@ public class MainActivity extends Activity implements DataApi.DataListener,
* indexes. For example, question0 should come before question1.
*/
private static class Question implements Comparable {
+
private String question;
private int questionIndex;
private String[] answers;
@@ -253,6 +254,7 @@ public class MainActivity extends Activity implements DataApi.DataListener,
/**
* Create a quiz, as defined in Quiz.json, when the user clicks on "Read quiz from file."
+ *
* @throws IOException
*/
public void readQuizFromFile(View view) throws IOException, JSONException {
@@ -411,17 +413,18 @@ public class MainActivity extends Activity implements DataApi.DataListener,
private void sendMessageToWearable(final String path, final byte[] data) {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
new ResultCallback() {
- @Override
- public void onResult(NodeApi.GetConnectedNodesResult nodes) {
- for (Node node : nodes.getNodes()) {
- Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), path, data);
- }
+ @Override
+ public void onResult(NodeApi.GetConnectedNodesResult nodes) {
+ for (Node node : nodes.getNodes()) {
+ Wearable.MessageApi
+ .sendMessage(mGoogleApiClient, node.getId(), path, data);
+ }
- if (path.equals(QUIZ_EXITED_PATH) && mGoogleApiClient.isConnected()) {
- mGoogleApiClient.disconnect();
- }
- }
- });
+ if (path.equals(QUIZ_EXITED_PATH) && mGoogleApiClient.isConnected()) {
+ mGoogleApiClient.disconnect();
+ }
+ }
+ });
}
/**
@@ -429,7 +432,7 @@ public class MainActivity extends Activity implements DataApi.DataListener,
*/
public void resetQuiz(View view) {
// Reset quiz status in phone layout.
- for(int i = 0; i < questionsContainer.getChildCount(); i++) {
+ for (int i = 0; i < questionsContainer.getChildCount(); i++) {
LinearLayout questionStatusElement = (LinearLayout) questionsContainer.getChildAt(i);
TextView questionText = (TextView) questionStatusElement.findViewById(R.id.question);
TextView questionStatus = (TextView) questionStatusElement.findViewById(R.id.status);
@@ -481,11 +484,12 @@ public class MainActivity extends Activity implements DataApi.DataListener,
* Callback that marks a DataItem, which represents a question, as unanswered and not deleted.
*/
private class ResetDataItemCallback implements ResultCallback {
+
@Override
public void onResult(DataApi.DataItemResult dataItemResult) {
if (dataItemResult.getStatus().isSuccess()) {
PutDataMapRequest request = PutDataMapRequest.createFromDataMapItem(
- DataMapItem.fromDataItem(dataItemResult.getDataItem()));
+ DataMapItem.fromDataItem(dataItemResult.getDataItem()));
DataMap dataMap = request.getDataMap();
dataMap.putBoolean(QUESTION_WAS_ANSWERED, false);
dataMap.putBoolean(QUESTION_WAS_DELETED, false);
diff --git a/samples/browseable/Quiz/Wearable/AndroidManifest.xml b/samples/browseable/Quiz/Wearable/AndroidManifest.xml
index fbc74922f..289a07f5e 100644
--- a/samples/browseable/Quiz/Wearable/AndroidManifest.xml
+++ b/samples/browseable/Quiz/Wearable/AndroidManifest.xml
@@ -19,7 +19,7 @@
+ android:targetSdkVersion="22" />
diff --git a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/Constants.java b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/Constants.java
index 8b81f59be..6791422f0 100644
--- a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/Constants.java
+++ b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/Constants.java
@@ -38,4 +38,5 @@ public final class Constants {
public static final String RESET_QUIZ_PATH = "/reset_quiz";
public static final int CONNECT_TIMEOUT_MS = 100;
+ public static final int GET_CAPABILITIES_TIMEOUT_MS = 5000;
}
diff --git a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/DeleteQuestionService.java b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/DeleteQuestionService.java
index 78e81ca0c..353903cda 100644
--- a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/DeleteQuestionService.java
+++ b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/DeleteQuestionService.java
@@ -16,6 +16,9 @@
package com.example.android.wearable.quiz;
+import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_DELETED;
+
import android.app.IntentService;
import android.content.Intent;
import android.net.Uri;
@@ -33,9 +36,6 @@ import com.google.android.gms.wearable.Wearable;
import java.util.concurrent.TimeUnit;
-import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
-import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_DELETED;
-
/**
* Used to update quiz status on the phone when user dismisses a question on the watch.
*/
diff --git a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizListenerService.java b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizListenerService.java
index 020a37b8d..95bedf0ac 100644
--- a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizListenerService.java
+++ b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizListenerService.java
@@ -64,6 +64,7 @@ import java.util.concurrent.TimeUnit;
* When the quiz ends, this listener receives a message telling it to create an end-of-quiz report.
*/
public class QuizListenerService extends WearableListenerService {
+
private static final String TAG = "QuizSample";
private static final int QUIZ_REPORT_NOTIF_ID = -1; // Never used by question notifications.
private static final Map questionNumToDrawableId;
diff --git a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizReportActionService.java b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizReportActionService.java
index 956bdaf38..3a31e3722 100644
--- a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizReportActionService.java
+++ b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/QuizReportActionService.java
@@ -16,29 +16,34 @@
package com.example.android.wearable.quiz;
+import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
+import static com.example.android.wearable.quiz.Constants.GET_CAPABILITIES_TIMEOUT_MS;
+import static com.example.android.wearable.quiz.Constants.RESET_QUIZ_PATH;
+
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.CapabilityInfo;
import com.google.android.gms.wearable.Node;
-import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
-import static com.example.android.wearable.quiz.Constants.CONNECT_TIMEOUT_MS;
-import static com.example.android.wearable.quiz.Constants.RESET_QUIZ_PATH;
-
/**
* Service to reset the quiz (by sending a message to the phone) when the Reset Quiz
* action on the Quiz Report is selected.
*/
public class QuizReportActionService extends IntentService {
+
public static final String ACTION_RESET_QUIZ = "com.example.android.wearable.quiz.RESET_QUIZ";
- private static final String TAG = "QuizReportActionReceiver";
+ private static final String TAG = "QuizReportActionService";
+ private static final String RESET_QUIZ_CAPABILITY_NAME = "reset_quiz";
public QuizReportActionService() {
super(QuizReportActionService.class.getSimpleName());
@@ -47,18 +52,35 @@ public class QuizReportActionService extends IntentService {
@Override
public void onHandleIntent(Intent intent) {
if (intent.getAction().equals(ACTION_RESET_QUIZ)) {
- GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
+ final GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.build();
ConnectionResult result = googleApiClient.blockingConnect(CONNECT_TIMEOUT_MS,
TimeUnit.MILLISECONDS);
if (!result.isSuccess()) {
- Log.e(TAG, "QuizListenerService failed to connect to GoogleApiClient.");
+ Log.e(TAG, "QuizReportActionService failed to connect to GoogleApiClient.");
return;
}
- NodeApi.GetConnectedNodesResult nodes =
- Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
- for (Node node : nodes.getNodes()) {
+
+ CapabilityApi.GetCapabilityResult capabilityResult = Wearable.CapabilityApi
+ .getCapability(googleApiClient, RESET_QUIZ_CAPABILITY_NAME,
+ CapabilityApi.FILTER_REACHABLE)
+ .await(GET_CAPABILITIES_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (capabilityResult.getStatus().isSuccess()) {
+ sendResetMessage(googleApiClient, capabilityResult.getCapability());
+ } else {
+ Log.e(TAG, "Failed to get capabilities, status: "
+ + capabilityResult.getStatus().getStatusMessage());
+ }
+ }
+ }
+
+ private void sendResetMessage(GoogleApiClient googleApiClient, CapabilityInfo capabilityInfo) {
+ Set connectedNodes = capabilityInfo.getNodes();
+ if (connectedNodes.isEmpty()) {
+ Log.w(TAG, "No node capable of resetting quiz was found");
+ } else {
+ for (Node node : connectedNodes) {
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), RESET_QUIZ_PATH,
new byte[0]);
}
diff --git a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/UpdateQuestionService.java b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/UpdateQuestionService.java
index e06c30346..7b8f730c9 100644
--- a/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/UpdateQuestionService.java
+++ b/samples/browseable/Quiz/Wearable/src/com.example.android.wearable.quiz/UpdateQuestionService.java
@@ -16,6 +16,10 @@
package com.example.android.wearable.quiz;
+import static com.example.android.wearable.quiz.Constants.CHOSEN_ANSWER_CORRECT;
+import static com.example.android.wearable.quiz.Constants.QUESTION_INDEX;
+import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_ANSWERED;
+
import android.app.IntentService;
import android.app.NotificationManager;
import android.content.Intent;
@@ -34,15 +38,12 @@ import com.google.android.gms.wearable.Wearable;
import java.util.concurrent.TimeUnit;
-import static com.example.android.wearable.quiz.Constants.CHOSEN_ANSWER_CORRECT;
-import static com.example.android.wearable.quiz.Constants.QUESTION_INDEX;
-import static com.example.android.wearable.quiz.Constants.QUESTION_WAS_ANSWERED;
-
/**
* Updates quiz status on the phone when user selects an answer to a question on the watch.
*/
public class UpdateQuestionService extends IntentService
implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
+
public static final String EXTRA_QUESTION_CORRECT = "extra_question_correct";
public static final String EXTRA_QUESTION_INDEX = "extra_question_index";
diff --git a/samples/browseable/XYZTouristAttractions/Application/res/values/wear.xml b/samples/browseable/XYZTouristAttractions/Application/res/values/wear.xml
new file mode 100644
index 000000000..719ca4b15
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Application/res/values/wear.xml
@@ -0,0 +1,7 @@
+
+
+
+ - @string/show_detail_capability_name
+
+
+
diff --git a/samples/browseable/XYZTouristAttractions/Shared/res/values/strings.xml b/samples/browseable/XYZTouristAttractions/Shared/res/values/strings.xml
new file mode 100644
index 000000000..87ede60c2
--- /dev/null
+++ b/samples/browseable/XYZTouristAttractions/Shared/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ show_details
+
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
index 234002ce3..8a4fea56a 100644
--- 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
@@ -20,14 +20,19 @@ import android.app.IntentService;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
+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.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.wearable.CapabilityApi;
+import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.Wearable;
import java.util.Iterator;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -35,6 +40,7 @@ import java.util.concurrent.TimeUnit;
* 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";
@@ -42,6 +48,7 @@ public class UtilityService extends IntentService {
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";
+ private static final long GET_CAPABILITY_TIMEOUT_S = 10;
public static void clearNotification(Context context) {
Intent intent = new Intent(context, UtilityService.class);
@@ -131,7 +138,8 @@ public class UtilityService extends IntentService {
}
/**
- * Sends the actual message to ask other devices to start an activity
+ * Sends the actual message to ask other devices that are capable of showing "details" to start
+ * the appropriate activity
*
* @param path the path to pass to the wearable message API
* @param extraInfo extra info that varies based on the path being sent
@@ -145,14 +153,24 @@ public class UtilityService extends IntentService {
Constants.GOOGLE_API_CLIENT_TIMEOUT_S, TimeUnit.SECONDS);
if (connectionResult.isSuccess() && googleApiClient.isConnected()) {
- Iterator itr = Utils.getNodes(googleApiClient).iterator();
- while (itr.hasNext()) {
- // Loop through all connected nodes
- Wearable.MessageApi.sendMessage(
- googleApiClient, itr.next(), path, extraInfo.getBytes());
+ CapabilityApi.GetCapabilityResult result = Wearable.CapabilityApi.getCapability(
+ googleApiClient,
+ getApplicationContext().getString(R.string.show_detail_capability_name),
+ CapabilityApi.FILTER_REACHABLE)
+ .await(GET_CAPABILITY_TIMEOUT_S, TimeUnit.SECONDS);
+ if (result.getStatus().isSuccess()) {
+ Set nodes = result.getCapability().getNodes();
+ for (Node node : nodes) {
+ Wearable.MessageApi.sendMessage(
+ googleApiClient, node.getId(), path, extraInfo.getBytes());
+ }
+ } else {
+ Log.e(TAG, "startDeviceActivityInternal() Failed to get capabilities, status: "
+ + result.getStatus().getStatusMessage());
}
+
+ googleApiClient.disconnect();
}
- googleApiClient.disconnect();
}
}