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