close
The Wayback Machine - https://web.archive.org/web/20171110210140/https://developer.android.com/samples/RuntimePermissionsWear/Application/src/com.example.android.wearable.runtimepermissions/MainPhoneActivity.html
Skip to content

Most visited

Recently visited

navigation
RuntimePermissionsWear / Application / src / com.example.android.wearable.runtimepermissions /

MainPhoneActivity.java

1
/*
2
 * Copyright (C) 2015 Google Inc. All Rights Reserved.
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.runtimepermissions;
18
 
19
import android.Manifest;
20
import android.app.Activity;
21
import android.content.Intent;
22
import android.content.pm.PackageManager;
23
import android.os.Environment;
24
import android.os.Looper;
25
import android.support.v4.app.ActivityCompat;
26
import android.support.v7.app.AppCompatActivity;
27
import android.os.Bundle;
28
import android.util.Log;
29
import android.view.View;
30
import android.widget.Button;
31
import android.widget.TextView;
32
 
33
import com.example.android.wearable.runtimepermissions.common.Constants;
34
 
35
import com.google.android.gms.common.ConnectionResult;
36
import com.google.android.gms.common.api.GoogleApiClient;
37
import com.google.android.gms.common.api.PendingResult;
38
import com.google.android.gms.common.api.ResultCallback;
39
import com.google.android.gms.wearable.CapabilityApi;
40
import com.google.android.gms.wearable.CapabilityInfo;
41
import com.google.android.gms.wearable.DataMap;
42
import com.google.android.gms.wearable.MessageApi;
43
import com.google.android.gms.wearable.MessageEvent;
44
import com.google.android.gms.wearable.Node;
45
import com.google.android.gms.wearable.Wearable;
46
 
47
import java.io.File;
48
import java.util.Set;
49
import java.util.concurrent.TimeUnit;
50
 
51
/**
52
 * Displays data that requires runtime permissions both locally (READ_EXTERNAL_STORAGE) and
53
 * remotely on wear (BODY_SENSORS).
54
 *
55
 * The class also handles sending back the results of a permission request from a remote wear device
56
 * when the permission has not been approved yet on the phone (uses EXTRA as trigger). In that case,
57
 * the IncomingRequestPhoneService launches the splash Activity (PhonePermissionRequestActivity) to
58
 * inform user of permission request. After the user decides what to do, it falls back to this
59
 * Activity (which has all the GoogleApiClient code) to handle sending data across and keeps user
60
 * in app experience.
61
 */
62
public class MainPhoneActivity extends AppCompatActivity implements
63
        GoogleApiClient.ConnectionCallbacks,
64
        GoogleApiClient.OnConnectionFailedListener,
65
        CapabilityApi.CapabilityListener,
66
        MessageApi.MessageListener,
67
        ResultCallback<MessageApi.SendMessageResult> {
68
 
69
    private static final String TAG = "MainPhoneActivity";
70
 
71
    /*
72
     * Alerts Activity that the initial request for permissions came from wear, and the Activity
73
     * needs to send back the results (data or permission rejection).
74
     */
75
    public static final String EXTRA_PROMPT_PERMISSION_FROM_WEAR =
76
            "com.example.android.wearable.runtimepermissions.extra.PROMPT_PERMISSION_FROM_WEAR";
77
 
78
    private static final int REQUEST_WEAR_PERMISSION_RATIONALE = 1;
79
 
80
    private boolean mWearBodySensorsPermissionApproved;
81
    private boolean mPhoneStoragePermissionApproved;
82
 
83
    private boolean mWearRequestingPhoneStoragePermission;
84
 
85
    private Button mWearBodySensorsPermissionButton;
86
    private Button mPhoneStoragePermissionButton;
87
    private TextView mOutputTextView;
88
 
89
    private Set<Node> mWearNodeIds;
90
 
91
    private GoogleApiClient mGoogleApiClient;
92
 
93
    @Override
94
    protected void onCreate(Bundle savedInstanceState) {
95
        Log.d(TAG, "onCreate()");
96
        super.onCreate(savedInstanceState);
97
 
98
        /*
99
         * Since this is a remote permission, we initialize it to false and then check the remote
100
         * permission once the GoogleApiClient is connected.
101
         */
102
        mWearBodySensorsPermissionApproved = false;
103
 
104
        setContentView(R.layout.activity_main);
105
 
106
        // Checks if wear app requested phone permission (permission request opens later if true).
107
        mWearRequestingPhoneStoragePermission =
108
                getIntent().getBooleanExtra(EXTRA_PROMPT_PERMISSION_FROM_WEAR, false);
109
 
110
        mPhoneStoragePermissionButton =
111
                (Button) findViewById(R.id.phoneStoragePermissionButton);
112
 
113
        mWearBodySensorsPermissionButton =
114
                (Button) findViewById(R.id.wearBodySensorsPermissionButton);
115
 
116
        mOutputTextView = (TextView) findViewById(R.id.output);
117
 
118
        mGoogleApiClient = new GoogleApiClient.Builder(this)
119
                .addApi(Wearable.API)
120
                .addConnectionCallbacks(this)
121
                .addOnConnectionFailedListener(this)
122
                .build();
123
    }
124
 
125
    public void onClickWearBodySensors(View view) {
126
 
127
        logToUi("Requested info from wear device(s). New approval may be required.");
128
 
129
        DataMap dataMap = new DataMap();
130
        dataMap.putInt(Constants.KEY_COMM_TYPE, Constants.COMM_TYPE_REQUEST_DATA);
131
        sendMessage(dataMap);
132
    }
133
 
134
    public void onClickPhoneStorage(View view) {
135
 
136
        if (mPhoneStoragePermissionApproved) {
137
            logToUi(getPhoneStorageInformation());
138
 
139
        } else {
140
            // On 23+ (M+) devices, Storage permission not granted. Request permission.
141
            Intent startIntent = new Intent(this, PhonePermissionRequestActivity.class);
142
            startActivity(startIntent);
143
        }
144
    }
145
 
146
    @Override
147
    protected void onPause() {
148
        Log.d(TAG, "onPause()");
149
        super.onPause();
150
        if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnected())) {
151
            Wearable.CapabilityApi.removeCapabilityListener(
152
                    mGoogleApiClient,
153
                    this,
154
                    Constants.CAPABILITY_WEAR_APP);
155
            Wearable.MessageApi.removeListener(mGoogleApiClient, this);
156
            mGoogleApiClient.disconnect();
157
        }
158
    }
159
 
160
    @Override
161
    protected void onResume() {
162
        Log.d(TAG, "onResume()");
163
        super.onResume();
164
 
165
        /* Enables app to handle 23+ (M+) style permissions. It also covers user changing
166
         * permission in settings and coming back to the app.
167
         */
168
        mPhoneStoragePermissionApproved =
169
                ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
170
                        == PackageManager.PERMISSION_GRANTED;
171
 
172
        if (mPhoneStoragePermissionApproved) {
173
            mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
174
                    R.drawable.ic_permission_approved, 0, 0, 0);
175
        }
176
 
177
        if (mGoogleApiClient != null) {
178
            mGoogleApiClient.connect();
179
        }
180
    }
181
 
182
    @Override
183
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
184
        Log.d(TAG, "onActivityResult()");
185
        if (requestCode == REQUEST_WEAR_PERMISSION_RATIONALE) {
186
 
187
            if (resultCode == Activity.RESULT_OK) {
188
                logToUi("Requested permission on wear device(s).");
189
 
190
                DataMap dataMap = new DataMap();
191
                dataMap.putInt(Constants.KEY_COMM_TYPE,
192
                        Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION);
193
                sendMessage(dataMap);
194
            }
195
        }
196
    }
197
 
198
    @Override
199
    public void onConnected(Bundle bundle) {
200
        Log.d(TAG, "onConnected()");
201
 
202
        // Set up listeners for capability and message changes.
203
        Wearable.CapabilityApi.addCapabilityListener(
204
                mGoogleApiClient,
205
                this,
206
                Constants.CAPABILITY_WEAR_APP);
207
        Wearable.MessageApi.addListener(mGoogleApiClient, this);
208
 
209
        // Initial check of capabilities to find the wear nodes.
210
        PendingResult<CapabilityApi.GetCapabilityResult> pendingResult =
211
                Wearable.CapabilityApi.getCapability(
212
                        mGoogleApiClient,
213
                        Constants.CAPABILITY_WEAR_APP,
214
                        CapabilityApi.FILTER_REACHABLE);
215
 
216
        pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() {
217
            @Override
218
            public void onResult(CapabilityApi.GetCapabilityResult getCapabilityResult) {
219
 
220
                CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
221
                String capabilityName = capabilityInfo.getName();
222
 
223
                boolean wearSupportsSampleApp =
224
                        capabilityName.equals(Constants.CAPABILITY_WEAR_APP);
225
 
226
                if (wearSupportsSampleApp) {
227
                    mWearNodeIds = capabilityInfo.getNodes();
228
 
229
                    /*
230
                     * Upon getting all wear nodes, we now need to check if the original request to
231
                     * launch this activity (and PhonePermissionRequestActivity) was initiated by
232
                     * a wear device. If it was, we need to send back the permission results (data
233
                     * or rejection of permission) to the wear device.
234
                     *
235
                     * Also, note we set variable to false, this enables the user to continue
236
                     * changing permissions without sending updates to the wear every time.
237
                     */
238
                    if (mWearRequestingPhoneStoragePermission) {
239
                        mWearRequestingPhoneStoragePermission = false;
240
                        sendWearPermissionResults();
241
                    }
242
                }
243
            }
244
        });
245
    }
246
 
247
    @Override
248
    public void onConnectionSuspended(int i) {
249
        Log.d(TAG, "onConnectionSuspended(): connection to location client suspended");
250
    }
251
 
252
    @Override
253
    public void onConnectionFailed(ConnectionResult connectionResult) {
254
        Log.e(TAG, "onConnectionFailed(): connection to location client failed");
255
    }
256
 
257
 
258
    public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
259
        Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo);
260
 
261
        mWearNodeIds = capabilityInfo.getNodes();
262
    }
263
 
264
    public void onMessageReceived(MessageEvent messageEvent) {
265
        Log.d(TAG, "onMessageReceived(): " + messageEvent);
266
 
267
        String messagePath = messageEvent.getPath();
268
 
269
        if (messagePath.equals(Constants.MESSAGE_PATH_PHONE)) {
270
            DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
271
 
272
            int commType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0);
273
 
274
            if (commType == Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED) {
275
                mWearBodySensorsPermissionApproved = false;
276
                updateWearButtonOnUiThread();
277
 
278
                /* Because our request for remote data requires a remote permission, we now launch
279
                 * a splash activity informing the user we need those permissions (along with
280
                 * other helpful information to approve).
281
                 */
282
                Intent wearPermissionRationale =
283
                        new Intent(this, WearPermissionRequestActivity.class);
284
                startActivityForResult(wearPermissionRationale, REQUEST_WEAR_PERMISSION_RATIONALE);
285
 
286
            } else if (commType == Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION) {
287
                mWearBodySensorsPermissionApproved = true;
288
                updateWearButtonOnUiThread();
289
                logToUi("User approved permission on remote device, requesting data again.");
290
                DataMap outgoingDataRequestDataMap = new DataMap();
291
                outgoingDataRequestDataMap.putInt(Constants.KEY_COMM_TYPE,
292
                        Constants.COMM_TYPE_REQUEST_DATA);
293
                sendMessage(outgoingDataRequestDataMap);
294
 
295
            } else if (commType == Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION) {
296
                mWearBodySensorsPermissionApproved = false;
297
                updateWearButtonOnUiThread();
298
                logToUi("User denied permission on remote device.");
299
 
300
            } else if (commType == Constants.COMM_TYPE_RESPONSE_DATA) {
301
                mWearBodySensorsPermissionApproved = true;
302
                String storageDetails = dataMap.getString(Constants.KEY_PAYLOAD);
303
                updateWearButtonOnUiThread();
304
                logToUi(storageDetails);
305
 
306
            } else {
307
                Log.d(TAG, "Unrecognized communication type received.");
308
            }
309
        }
310
    }
311
 
312
    @Override
313
    public void onResult(MessageApi.SendMessageResult sendMessageResult) {
314
        if (!sendMessageResult.getStatus().isSuccess()) {
315
            Log.d(TAG, "Sending message failed, onResult: " + sendMessageResult);
316
            updateWearButtonOnUiThread();
317
            logToUi("Sending message failed.");
318
 
319
        } else {
320
            Log.d(TAG, "Message sent.");
321
        }
322
    }
323
 
324
    private void sendMessage(DataMap dataMap) {
325
        Log.d(TAG, "sendMessage(): " + mWearNodeIds);
326
 
327
        if ((mWearNodeIds != null) && (!mWearNodeIds.isEmpty())) {
328
 
329
            PendingResult<MessageApi.SendMessageResult> pendingResult;
330
 
331
            for (Node node : mWearNodeIds) {
332
 
333
                pendingResult = Wearable.MessageApi.sendMessage(
334
                        mGoogleApiClient,
335
                        node.getId(),
336
                        Constants.MESSAGE_PATH_WEAR,
337
                        dataMap.toByteArray());
338
 
339
                pendingResult.setResultCallback(this, Constants.CONNECTION_TIME_OUT_MS,
340
                        TimeUnit.SECONDS);
341
            }
342
        } else {
343
            // Unable to retrieve node with proper capability
344
            mWearBodySensorsPermissionApproved = false;
345
            updateWearButtonOnUiThread();
346
            logToUi("Wear devices not available to send message.");
347
        }
348
    }
349
 
350
    private void updateWearButtonOnUiThread() {
351
        runOnUiThread(new Runnable() {
352
            @Override
353
            public void run() {
354
                if (mWearBodySensorsPermissionApproved) {
355
                    mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
356
                            R.drawable.ic_permission_approved, 0, 0, 0);
357
                } else {
358
                    mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
359
                            R.drawable.ic_permission_denied, 0, 0, 0);
360
                }
361
            }
362
        });
363
    }
364
 
365
    /*
366
     * Handles all messages for the UI coming on and off the main thread. Not all callbacks happen
367
     * on the main thread.
368
     */
369
    private void logToUi(final String message) {
370
 
371
        boolean mainUiThread = (Looper.myLooper() == Looper.getMainLooper());
372
 
373
        if (mainUiThread) {
374
 
375
            if (!message.isEmpty()) {
376
                Log.d(TAG, message);
377
                mOutputTextView.setText(message);
378
            }
379
 
380
        } else {
381
            if (!message.isEmpty()) {
382
 
383
                runOnUiThread(new Runnable() {
384
                    @Override
385
                    public void run() {
386
 
387
                        Log.d(TAG, message);
388
                        mOutputTextView.setText(message);
389
                    }
390
                });
391
            }
392
        }
393
    }
394
 
395
    private String getPhoneStorageInformation() {
396
 
397
        StringBuilder stringBuilder = new StringBuilder();
398
 
399
        String state = Environment.getExternalStorageState();
400
        boolean isExternalStorageReadable = Environment.MEDIA_MOUNTED.equals(state)
401
                || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
402
 
403
        if (isExternalStorageReadable) {
404
            File externalStorageDirectory = Environment.getExternalStorageDirectory();
405
            String[] fileList = externalStorageDirectory.list();
406
 
407
            if (fileList.length > 0) {
408
 
409
                stringBuilder.append("List of files\n");
410
                for (String file : fileList) {
411
                    stringBuilder.append(" - " + file + "\n");
412
                }
413
 
414
            } else {
415
                stringBuilder.append("No files in external storage.");
416
            }
417
 
418
        } else {
419
            stringBuilder.append("No external media is available.");
420
        }
421
 
422
        return stringBuilder.toString();
423
    }
424
 
425
    private void sendWearPermissionResults() {
426
 
427
        Log.d(TAG, "sendWearPermissionResults()");
428
 
429
        DataMap dataMap = new DataMap();
430
 
431
        if (mPhoneStoragePermissionApproved) {
432
            dataMap.putInt(Constants.KEY_COMM_TYPE,
433
                    Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
434
        } else {
435
            dataMap.putInt(Constants.KEY_COMM_TYPE,
436
                    Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION);
437
        }
438
        sendMessage(dataMap);
439
    }
440
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

BERJAYA

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)