Skip to content

Most visited

Recently visited

navigation
DataLayer / Application / src / com.example.android.wearable.datalayer /

MainActivity.java

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 android.app.Activity;
20
import android.content.Context;
21
import android.content.Intent;
22
import android.content.IntentSender;
23
import android.content.pm.PackageManager;
24
import android.graphics.Bitmap;
25
import android.net.Uri;
26
import android.os.AsyncTask;
27
import android.os.Bundle;
28
import android.provider.MediaStore;
29
import android.util.Log;
30
import android.view.LayoutInflater;
31
import android.view.View;
32
import android.view.ViewGroup;
33
import android.widget.ArrayAdapter;
34
import android.widget.Button;
35
import android.widget.ImageView;
36
import android.widget.ListView;
37
import android.widget.TextView;
38
 
39
import com.google.android.gms.common.ConnectionResult;
40
import com.google.android.gms.common.api.GoogleApiClient;
41
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
42
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
43
import com.google.android.gms.common.api.ResultCallback;
44
import com.google.android.gms.wearable.Asset;
45
import com.google.android.gms.wearable.CapabilityApi;
46
import com.google.android.gms.wearable.CapabilityInfo;
47
import com.google.android.gms.wearable.DataApi;
48
import com.google.android.gms.wearable.DataApi.DataItemResult;
49
import com.google.android.gms.wearable.DataEvent;
50
import com.google.android.gms.wearable.DataEventBuffer;
51
import com.google.android.gms.wearable.MessageApi;
52
import com.google.android.gms.wearable.MessageApi.SendMessageResult;
53
import com.google.android.gms.wearable.MessageEvent;
54
import com.google.android.gms.wearable.Node;
55
import com.google.android.gms.wearable.NodeApi;
56
import com.google.android.gms.wearable.PutDataMapRequest;
57
import com.google.android.gms.wearable.PutDataRequest;
58
import com.google.android.gms.wearable.Wearable;
59
 
60
import java.io.ByteArrayOutputStream;
61
import java.io.IOException;
62
import java.util.Collection;
63
import java.util.Date;
64
import java.util.HashSet;
65
import java.util.concurrent.ScheduledExecutorService;
66
import java.util.concurrent.ScheduledFuture;
67
import java.util.concurrent.ScheduledThreadPoolExecutor;
68
import java.util.concurrent.TimeUnit;
69
 
70
/**
71
 * Receives its own events using a listener API designed for foreground activities. Updates a data
72
 * item every second while it is open. Also allows user to take a photo and send that as an asset
73
 * to the paired wearable.
74
 */
75
public class MainActivity extends Activity implements
76
        CapabilityApi.CapabilityListener,
77
        MessageApi.MessageListener,
78
        DataApi.DataListener,
79
        ConnectionCallbacks,
80
        OnConnectionFailedListener {
81
 
82
    private static final String TAG = "MainActivity";
83
 
84
    //Request code for launching the Intent to resolve Google Play services errors.
85
    private static final int REQUEST_RESOLVE_ERROR = 1000;
86
 
87
    private static final int REQUEST_IMAGE_CAPTURE = 1;
88
 
89
    private static final String START_ACTIVITY_PATH = "/start-activity";
90
    private static final String COUNT_PATH = "/count";
91
    private static final String IMAGE_PATH = "/image";
92
    private static final String IMAGE_KEY = "photo";
93
    private static final String COUNT_KEY = "count";
94
 
95
    private GoogleApiClient mGoogleApiClient;
96
    private boolean mResolvingError = false;
97
    private boolean mCameraSupported = false;
98
 
99
    private ListView mDataItemList;
100
    private Button mSendPhotoBtn;
101
    private ImageView mThumbView;
102
    private Bitmap mImageBitmap;
103
    private View mStartActivityBtn;
104
 
105
    private DataItemAdapter mDataItemListAdapter;
106
 
107
    // Send DataItems.
108
    private ScheduledExecutorService mGeneratorExecutor;
109
    private ScheduledFuture<?> mDataItemGeneratorFuture;
110
 
111
    @Override
112
    public void onCreate(Bundle savedInstanceState) {
113
        super.onCreate(savedInstanceState);
114
        LOGD(TAG, "onCreate");
115
        mCameraSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
116
        setContentView(R.layout.main_activity);
117
        setupViews();
118
 
119
        // Stores DataItems received by the local broadcaster or from the paired watch.
120
        mDataItemListAdapter = new DataItemAdapter(this, android.R.layout.simple_list_item_1);
121
        mDataItemList.setAdapter(mDataItemListAdapter);
122
 
123
        mGeneratorExecutor = new ScheduledThreadPoolExecutor(1);
124
 
125
        mGoogleApiClient = new GoogleApiClient.Builder(this)
126
                .addApi(Wearable.API)
127
                .addConnectionCallbacks(this)
128
                .addOnConnectionFailedListener(this)
129
                .build();
130
    }
131
 
132
    @Override
133
    protected void onStart() {
134
        super.onStart();
135
        if (!mResolvingError) {
136
            mGoogleApiClient.connect();
137
        }
138
    }
139
 
140
    @Override
141
    public void onResume() {
142
        super.onResume();
143
        mDataItemGeneratorFuture = mGeneratorExecutor.scheduleWithFixedDelay(
144
                new DataItemGenerator(), 1, 5, TimeUnit.SECONDS);
145
    }
146
 
147
    @Override
148
    public void onPause() {
149
        super.onPause();
150
        mDataItemGeneratorFuture.cancel(true /* mayInterruptIfRunning */);
151
    }
152
 
153
    @Override
154
    protected void onStop() {
155
        if (!mResolvingError && (mGoogleApiClient != null) && (mGoogleApiClient.isConnected())) {
156
            Wearable.DataApi.removeListener(mGoogleApiClient, this);
157
            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
158
            Wearable.CapabilityApi.removeListener(mGoogleApiClient, this);
159
            mGoogleApiClient.disconnect();
160
        }
161
        super.onStop();
162
    }
163
 
164
    @Override
165
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
166
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
167
            Bundle extras = data.getExtras();
168
            mImageBitmap = (Bitmap) extras.get("data");
169
            mThumbView.setImageBitmap(mImageBitmap);
170
        }
171
    }
172
 
173
    @Override
174
    public void onConnected(Bundle connectionHint) {
175
        LOGD(TAG, "Google API Client was connected");
176
        mResolvingError = false;
177
        mStartActivityBtn.setEnabled(true);
178
        mSendPhotoBtn.setEnabled(mCameraSupported);
179
        Wearable.DataApi.addListener(mGoogleApiClient, this);
180
        Wearable.MessageApi.addListener(mGoogleApiClient, this);
181
        Wearable.CapabilityApi.addListener(
182
                mGoogleApiClient, this, Uri.parse("wear://"), CapabilityApi.FILTER_REACHABLE);
183
    }
184
 
185
    @Override
186
    public void onConnectionSuspended(int cause) {
187
        LOGD(TAG, "Connection to Google API client was suspended");
188
        mStartActivityBtn.setEnabled(false);
189
        mSendPhotoBtn.setEnabled(false);
190
    }
191
 
192
    @Override
193
    public void onConnectionFailed(ConnectionResult result) {
194
        if (!mResolvingError) {
195
 
196
            if (result.hasResolution()) {
197
                try {
198
                    mResolvingError = true;
199
                    result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
200
                } catch (IntentSender.SendIntentException e) {
201
                    // There was an error with the resolution intent. Try again.
202
                    mGoogleApiClient.connect();
203
                }
204
            } else {
205
                Log.e(TAG, "Connection to Google API client has failed");
206
                mResolvingError = false;
207
                mStartActivityBtn.setEnabled(false);
208
                mSendPhotoBtn.setEnabled(false);
209
                Wearable.DataApi.removeListener(mGoogleApiClient, this);
210
                Wearable.MessageApi.removeListener(mGoogleApiClient, this);
211
                Wearable.CapabilityApi.removeListener(mGoogleApiClient, this);
212
            }
213
        }
214
    }
215
 
216
    @Override
217
    public void onDataChanged(DataEventBuffer dataEvents) {
218
        LOGD(TAG, "onDataChanged: " + dataEvents);
219
 
220
        for (DataEvent event : dataEvents) {
221
            if (event.getType() == DataEvent.TYPE_CHANGED) {
222
                mDataItemListAdapter.add(
223
                        new Event("DataItem Changed", event.getDataItem().toString()));
224
            } else if (event.getType() == DataEvent.TYPE_DELETED) {
225
                mDataItemListAdapter.add(
226
                        new Event("DataItem Deleted", event.getDataItem().toString()));
227
            }
228
        }
229
    }
230
 
231
    @Override
232
    public void onMessageReceived(final MessageEvent messageEvent) {
233
        LOGD(TAG, "onMessageReceived() A message from watch was received:"
234
                + messageEvent.getRequestId() + " " + messageEvent.getPath());
235
 
236
        mDataItemListAdapter.add(new Event("Message from watch", messageEvent.toString()));
237
    }
238
 
239
    @Override
240
    public void onCapabilityChanged(final CapabilityInfo capabilityInfo) {
241
        LOGD(TAG, "onCapabilityChanged: " + capabilityInfo);
242
 
243
        mDataItemListAdapter.add(new Event("onCapabilityChanged", capabilityInfo.toString()));
244
    }
245
 
246
    /**
247
     * Sets up UI components and their callback handlers.
248
     */
249
    private void setupViews() {
250
        mSendPhotoBtn = (Button) findViewById(R.id.sendPhoto);
251
        mThumbView = (ImageView) findViewById(R.id.imageView);
252
        mDataItemList = (ListView) findViewById(R.id.data_item_list);
253
        mStartActivityBtn = findViewById(R.id.start_wearable_activity);
254
    }
255
 
256
    public void onTakePhotoClick(View view) {
257
        dispatchTakePictureIntent();
258
    }
259
 
260
    public void onSendPhotoClick(View view) {
261
        if (null != mImageBitmap && mGoogleApiClient.isConnected()) {
262
            sendPhoto(toAsset(mImageBitmap));
263
        }
264
    }
265
 
266
    /**
267
     * Sends an RPC to start a fullscreen Activity on the wearable.
268
     */
269
    public void onStartWearableActivityClick(View view) {
270
        LOGD(TAG, "Generating RPC");
271
 
272
        // Trigger an AsyncTask that will query for a list of connected nodes and send a
273
        // "start-activity" message to each connected node.
274
        new StartWearableActivityTask().execute();
275
    }
276
 
277
    private void sendStartActivityMessage(String node) {
278
        Wearable.MessageApi.sendMessage(
279
                mGoogleApiClient, node, START_ACTIVITY_PATH, new byte[0]).setResultCallback(
280
                new ResultCallback<SendMessageResult>() {
281
                    @Override
282
                    public void onResult(SendMessageResult sendMessageResult) {
283
                        if (!sendMessageResult.getStatus().isSuccess()) {
284
                            Log.e(TAG, "Failed to send message with status code: "
285
                                    + sendMessageResult.getStatus().getStatusCode());
286
                        }
287
                    }
288
                }
289
        );
290
    }
291
 
292
    /**
293
     * Dispatches an {@link android.content.Intent} to take a photo. Result will be returned back
294
     * in onActivityResult().
295
     */
296
    private void dispatchTakePictureIntent() {
297
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
298
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
299
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
300
        }
301
    }
302
 
303
    /**
304
     * Builds an {@link com.google.android.gms.wearable.Asset} from a bitmap. The image that we get
305
     * back from the camera in "data" is a thumbnail size. Typically, your image should not exceed
306
     * 320x320 and if you want to have zoom and parallax effect in your app, limit the size of your
307
     * image to 640x400. Resize your image before transferring to your wear