1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.wearable.datalayer; 18 19 import static com.example.android.wearable.datalayer.DataLayerListenerService.LOGD; 20 21 import android.app.Activity; 22 import android.app.Fragment; 23 import android.app.FragmentManager; 24 import android.graphics.Bitmap; 25 import android.graphics.BitmapFactory; 26 import android.net.Uri; 27 import android.os.AsyncTask; 28 import android.os.Bundle; 29 import android.support.wearable.view.DotsPageIndicator; 30 import android.support.wearable.view.FragmentGridPagerAdapter; 31 import android.support.wearable.view.GridViewPager; 32 import android.text.TextUtils; 33 import android.util.Log; 34 import android.view.View; 35 import android.view.WindowManager; 36 import android.widget.Toast; 37 38 import com.example.android.wearable.datalayer.fragments.AssetFragment; 39 import com.example.android.wearable.datalayer.fragments.DataFragment; 40 import com.example.android.wearable.datalayer.fragments.DiscoveryFragment; 41 import com.google.android.gms.common.ConnectionResult; 42 import com.google.android.gms.common.api.GoogleApiClient; 43 import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; 44 import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; 45 import com.google.android.gms.common.api.PendingResult; 46 import com.google.android.gms.common.api.ResultCallback; 47 import com.google.android.gms.wearable.Asset; 48 import com.google.android.gms.wearable.CapabilityApi; 49 import com.google.android.gms.wearable.CapabilityInfo; 50 import com.google.android.gms.wearable.DataApi; 51 import com.google.android.gms.wearable.DataEvent; 52 import com.google.android.gms.wearable.DataEventBuffer; 53 import com.google.android.gms.wearable.DataMapItem; 54 import com.google.android.gms.wearable.MessageApi; 55 import com.google.android.gms.wearable.MessageEvent; 56 import com.google.android.gms.wearable.Node; 57 import com.google.android.gms.wearable.Wearable; 58 59 import java.io.InputStream; 60 import java.util.ArrayList; 61 import java.util.HashSet; 62 import java.util.List; 63 import java.util.Map; 64 import java.util.Set; 65 66 /** 67 * The main activity with a view pager, containing three pages:<p/> 68 * <ul> 69 * <li> 70 * Page 1: shows a list of DataItems received from the phone application 71 * </li> 72 * <li> 73 * Page 2: shows the photo that is sent from the phone application 74 * </li> 75 * <li> 76 * Page 3: includes two buttons to show the connected phone and watch devices 77 * </li> 78 * </ul> 79 */ 80 public class MainActivity extends Activity implements 81 ConnectionCallbacks, 82 OnConnectionFailedListener, 83 DataApi.DataListener, 84 MessageApi.MessageListener, 85 CapabilityApi.CapabilityListener { 86 87 private static final String TAG = "MainActivity"; 88 private static final String CAPABILITY_1_NAME = "capability_1"; 89 private static final String CAPABILITY_2_NAME = "capability_2"; 90 91 private GoogleApiClient mGoogleApiClient; 92 private GridViewPager mPager; 93 private DataFragment mDataFragment; 94 private AssetFragment mAssetFragment; 95 96 @Override 97 public void onCreate(Bundle savedInstanceState) { 98 super.onCreate(savedInstanceState); 99 setContentView(R.layout.main_activity); 100 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 101 setupViews(); 102 mGoogleApiClient = new GoogleApiClient.Builder(this) 103 .addApi(Wearable.API) 104 .addConnectionCallbacks(this) 105 .addOnConnectionFailedListener(this) 106 .build(); 107 } 108 109 @Override 110 protected void onResume() { 111 super.onResume(); 112 mGoogleApiClient.connect(); 113 } 114 115 @Override 116 protected void onPause() { 117 if ((mGoogleApiClient != null) && mGoogleApiClient.isConnected()) { 118 Wearable.DataApi.removeListener(mGoogleApiClient, this); 119 Wearable.MessageApi.removeListener(mGoogleApiClient, this); 120 Wearable.CapabilityApi.removeListener(mGoogleApiClient, this); 121 mGoogleApiClient.disconnect(); 122 } 123 124 super.onPause(); 125 } 126 127 @Override 128 public void onConnected(Bundle connectionHint) { 129 LOGD(TAG, "onConnected(): Successfully connected to Google API client"); 130 Wearable.DataApi.addListener(mGoogleApiClient, this); 131 Wearable.MessageApi.addListener(mGoogleApiClient, this); 132 Wearable.CapabilityApi.addListener( 133 mGoogleApiClient, this, Uri.parse("wear://"), CapabilityApi.FILTER_REACHABLE); 134 } 135 136 @Override 137 public void onConnectionSuspended(int cause) { 138 LOGD(TAG, "onConnectionSuspended(): Connection to Google API client was suspended"); 139 } 140 141 @Override 142 public void onConnectionFailed(ConnectionResult result) { 143 Log.e(TAG, "onConnectionFailed(): Failed to connect, with result: " + result); 144 } 145 146 @Override 147 public void onDataChanged(DataEventBuffer dataEvents) { 148 LOGD(TAG, "onDataChanged(): " + dataEvents); 149 150 for (DataEvent event : dataEvents) { 151 if (event.getType() == DataEvent.TYPE_CHANGED) { 152 String path = event.getDataItem().getUri().getPath(); 153 if (DataLayerListenerService.IMAGE_PATH.equals(path)) { 154 DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem()); 155 Asset photoAsset = dataMapItem.getDataMap() 156 .getAsset(DataLayerListenerService.IMAGE_KEY); 157 // Loads image on background thread. 158 new LoadBitmapAsyncTask().execute(photoAsset); 159 160 } else if (DataLayerListenerService.COUNT_PATH.equals(path)) { 161 LOGD(TAG, "Data Changed for COUNT_PATH"); 162 mDataFragment.appendItem("DataItem Changed", event.getDataItem().toString()); 163 } else { 164 LOGD(TAG, "Unrecognized path: " + path); 165 } 166 167 } else if (event.getType() == DataEvent.TYPE_DELETED) { 168 mDataFragment.appendItem("DataItem Deleted", event.getDataItem().toString()); 169 } else { 170 mDataFragment.appendItem("Unknown data event type", "Type = " + event.getType()); 171 } 172 } 173 } 174 175 public void onClicked(View view) { 176 switch (view.getId()) { 177 case R.id.capability_2_btn: 178 showNodes(CAPABILITY_2_NAME); 179 break; 180 case R.id.capabilities_1_and_2_btn: 181 showNodes(CAPABILITY_1_NAME, CAPABILITY_2_NAME); 182 break; 183 default: 184 Log.e(TAG, "Unknown click event registered"); 185 } 186 } 187 188 /** 189 * Find the connected nodes that provide at least one of the given capabilities 190 */ 191 private void showNodes(final String... capabilityNames) { 192 193 PendingResult<CapabilityApi.GetAllCapabilitiesResult> pendingCapabilityResult = 194 Wearable.CapabilityApi.getAllCapabilities( 195 mGoogleApiClient, 196 CapabilityApi.FILTER_REACHABLE); 197 198 pendingCapabilityResult.setResultCallback( 199 new ResultCallback<CapabilityApi.GetAllCapabilitiesResult>() { 200 @Override 201 public void onResult( 202 CapabilityApi.GetAllCapabilitiesResult getAllCapabilitiesResult) { 203 204 if (!getAllCapabilitiesResult.getStatus().isSuccess()) { 205 Log.e(TAG, "Failed to get capabilities"); 206 return; 207 } 208 209 Map<String, CapabilityInfo> capabilitiesMap = 210 getAllCapabilitiesResult.getAllCapabilities(); 211 Set<Node> nodes = new HashSet<>(); 212 213 if (capabilitiesMap.isEmpty()) { 214 showDiscoveredNodes(nodes); 215 return; 216 } 217 for (String capabilityName : capabilityNames) { 218 CapabilityInfo capabilityInfo = capabilitiesMap.get(capabilityName); 219 if (capabilityInfo != null) { 220 nodes.addAll(capabilityInfo.getNodes()); 221 } 222 } 223 showDiscoveredNodes(nodes); 224 } 225 226 private void showDiscoveredNodes(Set<Node> nodes) { 227 List<String> nodesList = new ArrayList<>(); 228 for (Node node : nodes) { 229 nodesList.add(node.getDisplayName()); 230 } 231 LOGD(TAG, "Connected Nodes: " + (nodesList.isEmpty() 232 ? "No connected device was found for the given capabilities" 233 : TextUtils.join(",", nodesList))); 234 String msg; 235 if (!nodesList.isEmpty()) { 236 msg = getString(R.string.connected_nodes, 237 TextUtils.join(", ", nodesList)); 238 } else { 239 msg = getString(R.string.no_device); 240 } 241 Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show(); 242 } 243 }); 244 } 245 246 @Override 247 public void onMessageReceived(MessageEvent event) { 248 LOGD(TAG, "onMessageReceived: " + event); 249 mDataFragment.appendItem("Message", event.toString()); 250 } 251 252 @Override 253 public void onCapabilityChanged(CapabilityInfo capabilityInfo) { 254 LOGD(TAG, "onCapabilityChanged: " + capabilityInfo); 255 mDataFragment.appendItem("onCapabilityChanged", capabilityInfo.toString()); 256 } 257 258 private void setupViews() { 259 mPager = (GridViewPager) findViewById(R.id.pager); 260 mPager.setOffscreenPageCount(2); 261 DotsPageIndicator dotsPageIndicator = (DotsPageIndicator) findViewById(R.id.page_indicator); 262 dotsPageIndicator.setDotSpacing((int) getResources().getDimension(R.dimen.dots_spacing)); 263 dotsPageIndicator.setPager(mPager); 264 mDataFragment = new DataFragment(); 265 mAssetFragment = new AssetFragment(); 266 DiscoveryFragment discoveryFragment = new DiscoveryFragment(); 267 List<Fragment> pages = new ArrayList<>(); 268 pages.add(mDataFragment); 269 pages.add(mAssetFragment); 270 pages.add(discoveryFragment); 271 final MyPagerAdapter adapter = new MyPagerAdapter(getFragmentManager(), pages); 272 mPager.setAdapter(adapter); 273 } 274 275 /** 276 * Switches to the page {@code index}. The first page has index 0. 277 */ 278 private void moveToPage(int index) { 279 mPager.setCurrentItem(0, index, true); 280 } 281 282 private class MyPagerAdapter extends FragmentGridPagerAdapter { 283 284 private List<Fragment> mFragments; 285 286 public MyPagerAdapter(FragmentManager fm, List<Fragment> fragments) { 287 super(fm); 288 mFragments = fragments; 289 } 290 291 @Override 292 public int getRowCount() { 293 return 1; 294 } 295 296 @Override 297 public int getColumnCount(int row) { 298 return mFragments == null ? 0 : mFragments.size(); 299 } 300 301 @Override 302 public Fragment getFragment(int row, int column) { 303 return mFragments.get(column); 304 } 305 306 } 307 308 /* 309 * Extracts {@link android.graphics.Bitmap} data from the 310 * {@link com.google.android.gms.wearable.Asset} 311 */ 312 private class LoadBitmapAsyncTask extends AsyncTask<Asset, Void, Bitmap> { 313 314 @Override 315 protected Bitmap doInBackground(Asset... params) { 316 317 if (params.length > 0) { 318 319 Asset asset = params[0]; 320 321 InputStream assetInputStream = Wearable.DataApi.getFdForAsset( 322 mGoogleApiClient, asset).await().getInputStream(); 323 324 if (assetInputStream == null) { 325 Log.w(TAG, "Requested an unknown Asset."); 326 return null; 327 } 328 return BitmapFactory.decodeStream(assetInputStream); 329 330 } else { 331 Log.e(TAG, "Asset must be non-null"); 332 return null; 333 } 334 } 335 336 @Override 337 protected void onPostExecute(Bitmap bitmap) { 338 339 if (bitmap != null) { 340 LOGD(TAG, "Setting background image on second page.."); 341 moveToPage(1); 342 mAssetFragment.setBackgroundImage(bitmap); 343 } 344 } 345 } 346 }