Update sample prebuilts for mnc-docs
Synced to //developers/samples/android commit 243feb49e8d1753b746f69ae5519eaace0e50605. Change-Id: I9255d2ad8f68669d77124b7840184171fb5a801b
@@ -250,6 +250,11 @@ public class CalendarQueryService extends IntentService
|
||||
public PutDataMapRequest toPutDataMapRequest(){
|
||||
final PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(
|
||||
makeDataItemPath(eventId, begin));
|
||||
/* In most cases (as in this one), you don't need your DataItem appear instantly. By
|
||||
default, delivery of normal DataItems to the Wear network might be delayed in order to
|
||||
improve battery life for user devices. However, if you can't tolerate a delay in the
|
||||
sync of your DataItems, you can mark them as urgent via setUrgent().
|
||||
*/
|
||||
DataMap data = putDataMapRequest.getDataMap();
|
||||
data.putString(DATA_ITEM_URI, putDataMapRequest.getUri().toString());
|
||||
data.putLong(ID, id);
|
||||
|
||||
@@ -48,6 +48,7 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v13.app.FragmentCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.util.SparseIntArray;
|
||||
@@ -269,6 +270,11 @@ public class Camera2BasicFragment extends Fragment
|
||||
*/
|
||||
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
|
||||
|
||||
/**
|
||||
* Whether the current camera device supports Flash or not.
|
||||
*/
|
||||
private boolean mFlashSupported;
|
||||
|
||||
/**
|
||||
* A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
|
||||
*/
|
||||
@@ -568,6 +574,10 @@ public class Camera2BasicFragment extends Fragment
|
||||
mPreviewSize.getHeight(), mPreviewSize.getWidth());
|
||||
}
|
||||
|
||||
// Check if the flash is supported.
|
||||
Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
|
||||
mFlashSupported = available == null ? false : available;
|
||||
|
||||
mCameraId = cameraId;
|
||||
return;
|
||||
}
|
||||
@@ -585,7 +595,7 @@ public class Camera2BasicFragment extends Fragment
|
||||
* Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.
|
||||
*/
|
||||
private void openCamera(int width, int height) {
|
||||
if (getActivity().checkSelfPermission(Manifest.permission.CAMERA)
|
||||
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
requestCameraPermission();
|
||||
return;
|
||||
@@ -691,8 +701,7 @@ public class Camera2BasicFragment extends Fragment
|
||||
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
|
||||
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
|
||||
// Flash is automatically enabled when necessary.
|
||||
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
|
||||
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
setAutoFlash(mPreviewRequestBuilder);
|
||||
|
||||
// Finally, we start displaying the camera preview.
|
||||
mPreviewRequest = mPreviewRequestBuilder.build();
|
||||
@@ -808,8 +817,7 @@ public class Camera2BasicFragment extends Fragment
|
||||
// Use the same AE and AF modes as the preview.
|
||||
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
|
||||
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
|
||||
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
|
||||
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
setAutoFlash(captureBuilder);
|
||||
|
||||
// Orientation
|
||||
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
|
||||
@@ -844,8 +852,7 @@ public class Camera2BasicFragment extends Fragment
|
||||
// Reset the auto-focus trigger
|
||||
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
|
||||
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
|
||||
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
|
||||
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
setAutoFlash(mPreviewRequestBuilder);
|
||||
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
|
||||
mBackgroundHandler);
|
||||
// After this, the camera will go back to the normal state of preview.
|
||||
@@ -877,6 +884,13 @@ public class Camera2BasicFragment extends Fragment
|
||||
}
|
||||
}
|
||||
|
||||
private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
|
||||
if (mFlashSupported) {
|
||||
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
|
||||
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a JPEG {@link Image} into the specified {@link File}.
|
||||
*/
|
||||
|
||||
@@ -381,7 +381,9 @@ public class MainActivity extends Activity implements DataApi.DataListener,
|
||||
public void run() {
|
||||
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(COUNT_PATH);
|
||||
putDataMapRequest.getDataMap().putInt(COUNT_KEY, count++);
|
||||
putDataMapRequest.setUrgent();
|
||||
PutDataRequest request = putDataMapRequest.asPutDataRequest();
|
||||
request.setUrgent();
|
||||
|
||||
LOGD(TAG, "Generating DataItem: " + request);
|
||||
if (!mGoogleApiClient.isConnected()) {
|
||||
@@ -442,6 +444,8 @@ public class MainActivity extends Activity implements DataApi.DataListener,
|
||||
dataMap.getDataMap().putAsset(IMAGE_KEY, asset);
|
||||
dataMap.getDataMap().putLong("time", new Date().getTime());
|
||||
PutDataRequest request = dataMap.asPutDataRequest();
|
||||
request.setUrgent();
|
||||
|
||||
Wearable.DataApi.putDataItem(mGoogleApiClient, request)
|
||||
.setResultCallback(new ResultCallback<DataItemResult>() {
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
android:layout_height="fill_parent" >
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressbar"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.example.android.displayingbitmaps.R;
|
||||
import com.example.android.displayingbitmaps.util.ImageFetcher;
|
||||
@@ -32,10 +33,11 @@ import com.example.android.displayingbitmaps.util.Utils;
|
||||
/**
|
||||
* This fragment will populate the children of the ViewPager from {@link ImageDetailActivity}.
|
||||
*/
|
||||
public class ImageDetailFragment extends Fragment {
|
||||
public class ImageDetailFragment extends Fragment implements ImageWorker.OnImageLoadedListener {
|
||||
private static final String IMAGE_DATA_EXTRA = "extra_image_data";
|
||||
private String mImageUrl;
|
||||
private ImageView mImageView;
|
||||
private ProgressBar mProgressBar;
|
||||
private ImageFetcher mImageFetcher;
|
||||
|
||||
/**
|
||||
@@ -75,6 +77,7 @@ public class ImageDetailFragment extends Fragment {
|
||||
// Inflate and locate the main ImageView
|
||||
final View v = inflater.inflate(R.layout.image_detail_fragment, container, false);
|
||||
mImageView = (ImageView) v.findViewById(R.id.imageView);
|
||||
mProgressBar = (ProgressBar) v.findViewById(R.id.progressbar);
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -86,7 +89,7 @@ public class ImageDetailFragment extends Fragment {
|
||||
// cache can be used over all pages in the ViewPager
|
||||
if (ImageDetailActivity.class.isInstance(getActivity())) {
|
||||
mImageFetcher = ((ImageDetailActivity) getActivity()).getImageFetcher();
|
||||
mImageFetcher.loadImage(mImageUrl, mImageView);
|
||||
mImageFetcher.loadImage(mImageUrl, mImageView, this);
|
||||
}
|
||||
|
||||
// Pass clicks on the ImageView to the parent activity to handle
|
||||
@@ -104,4 +107,11 @@ public class ImageDetailFragment extends Fragment {
|
||||
mImageView.setImageDrawable(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageLoaded(boolean success) {
|
||||
// Set loading spinner to gone once image has loaded. Cloud also show
|
||||
// an error view here if needed.
|
||||
mProgressBar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,8 +71,9 @@ public abstract class ImageWorker {
|
||||
*
|
||||
* @param data The URL of the image to download.
|
||||
* @param imageView The ImageView to bind the downloaded image to.
|
||||
* @param listener A listener that will be called back once the image has been loaded.
|
||||
*/
|
||||
public void loadImage(Object data, ImageView imageView) {
|
||||
public void loadImage(Object data, ImageView imageView, OnImageLoadedListener listener) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
@@ -86,9 +87,12 @@ public abstract class ImageWorker {
|
||||
if (value != null) {
|
||||
// Bitmap found in memory cache
|
||||
imageView.setImageDrawable(value);
|
||||
if (listener != null) {
|
||||
listener.onImageLoaded(true);
|
||||
}
|
||||
} else if (cancelPotentialWork(data, imageView)) {
|
||||
//BEGIN_INCLUDE(execute_background_task)
|
||||
final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView);
|
||||
final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView, listener);
|
||||
final AsyncDrawable asyncDrawable =
|
||||
new AsyncDrawable(mResources, mLoadingBitmap, task);
|
||||
imageView.setImageDrawable(asyncDrawable);
|
||||
@@ -101,6 +105,21 @@ public abstract class ImageWorker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an image specified by the data parameter into an ImageView (override
|
||||
* {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and
|
||||
* disk cache will be used if an {@link ImageCache} has been added using
|
||||
* {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCache.ImageCacheParams)}. If the
|
||||
* image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
|
||||
* will be created to asynchronously load the bitmap.
|
||||
*
|
||||
* @param data The URL of the image to download.
|
||||
* @param imageView The ImageView to bind the downloaded image to.
|
||||
*/
|
||||
public void loadImage(Object data, ImageView imageView) {
|
||||
loadImage(data, imageView, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set placeholder bitmap that shows when the the background thread is running.
|
||||
*
|
||||
@@ -238,10 +257,18 @@ public abstract class ImageWorker {
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
|
||||
private Object mData;
|
||||
private final WeakReference<ImageView> imageViewReference;
|
||||
private final OnImageLoadedListener mOnImageLoadedListener;
|
||||
|
||||
public BitmapWorkerTask(Object data, ImageView imageView) {
|
||||
mData = data;
|
||||
imageViewReference = new WeakReference<ImageView>(imageView);
|
||||
mOnImageLoadedListener = null;
|
||||
}
|
||||
|
||||
public BitmapWorkerTask(Object data, ImageView imageView, OnImageLoadedListener listener) {
|
||||
mData = data;
|
||||
imageViewReference = new WeakReference<ImageView>(imageView);
|
||||
mOnImageLoadedListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,6 +345,7 @@ public abstract class ImageWorker {
|
||||
@Override
|
||||
protected void onPostExecute(BitmapDrawable value) {
|
||||
//BEGIN_INCLUDE(complete_background_work)
|
||||
boolean success = false;
|
||||
// if cancel was called on this task or the "exit early" flag is set then we're done
|
||||
if (isCancelled() || mExitTasksEarly) {
|
||||
value = null;
|
||||
@@ -328,8 +356,12 @@ public abstract class ImageWorker {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "onPostExecute - setting bitmap");
|
||||
}
|
||||
success = true;
|
||||
setImageDrawable(imageView, value);
|
||||
}
|
||||
if (mOnImageLoadedListener != null) {
|
||||
mOnImageLoadedListener.onImageLoaded(success);
|
||||
}
|
||||
//END_INCLUDE(complete_background_work)
|
||||
}
|
||||
|
||||
@@ -357,6 +389,19 @@ public abstract class ImageWorker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface definition for callback on image loaded successfully.
|
||||
*/
|
||||
public interface OnImageLoadedListener {
|
||||
|
||||
/**
|
||||
* Called once the image has been loaded.
|
||||
* @param success True if the image was loaded successfully, false if
|
||||
* there was an error.
|
||||
*/
|
||||
void onImageLoaded(boolean success);
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom Drawable that will be attached to the imageView while the work is in progress.
|
||||
* Contains a reference to the actual worker task, so that it can be stopped if a new binding is
|
||||
|
||||
@@ -100,6 +100,7 @@ public class FindPhoneService extends IntentService implements GoogleApiClient.C
|
||||
// when it receives the change.
|
||||
PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(PATH_SOUND_ALARM);
|
||||
putDataMapRequest.getDataMap().putBoolean(FIELD_ALARM_ON, alarmOn);
|
||||
putDataMapRequest.setUrgent();
|
||||
Wearable.DataApi.putDataItem(mGoogleApiClient, putDataMapRequest.asPutDataRequest())
|
||||
.await();
|
||||
} else {
|
||||
|
||||
@@ -89,6 +89,7 @@ public class GeofenceTransitionsIntentService extends IntentService
|
||||
final PutDataMapRequest putDataMapRequest =
|
||||
PutDataMapRequest.create(GEOFENCE_DATA_ITEM_PATH);
|
||||
putDataMapRequest.getDataMap().putString(KEY_GEOFENCE_ID, triggeredGeoFenceId);
|
||||
putDataMapRequest.setUrgent();
|
||||
if (mGoogleApiClient.isConnected()) {
|
||||
Wearable.DataApi.putDataItem(
|
||||
mGoogleApiClient, putDataMapRequest.asPutDataRequest()).await();
|
||||
|
||||
@@ -248,7 +248,9 @@ public class MainActivity extends Activity implements DataApi.DataListener,
|
||||
dataMap.putInt(QUESTION_INDEX, questionIndex);
|
||||
dataMap.putStringArray(ANSWERS, answers);
|
||||
dataMap.putInt(CORRECT_ANSWER_INDEX, correctAnswerIndex);
|
||||
return request.asPutDataRequest();
|
||||
PutDataRequest putDataRequest = request.asPutDataRequest();
|
||||
putDataRequest.setUrgent();
|
||||
return putDataRequest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,7 +498,10 @@ public class MainActivity extends Activity implements DataApi.DataListener,
|
||||
dataMap.putBoolean(QUESTION_WAS_DELETED, false);
|
||||
if (!mHasQuestionBeenAsked && dataMap.getInt(QUESTION_INDEX) == 0) {
|
||||
// Ask the first question now.
|
||||
Wearable.DataApi.putDataItem(mGoogleApiClient, request.asPutDataRequest());
|
||||
PutDataRequest putDataRequest = request.asPutDataRequest();
|
||||
// Set to high priority in case it isn't already.
|
||||
putDataRequest.setUrgent();
|
||||
Wearable.DataApi.putDataItem(mGoogleApiClient, putDataRequest);
|
||||
setHasQuestionBeenAsked(true);
|
||||
} else {
|
||||
// Enqueue future questions.
|
||||
|
||||
@@ -76,6 +76,7 @@ public class DeleteQuestionService extends IntentService
|
||||
DataMap dataMap = putDataMapRequest.getDataMap();
|
||||
dataMap.putBoolean(QUESTION_WAS_DELETED, true);
|
||||
PutDataRequest request = putDataMapRequest.asPutDataRequest();
|
||||
request.setUrgent();
|
||||
Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
|
||||
mGoogleApiClient.disconnect();
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ public class UpdateQuestionService extends IntentService
|
||||
dataMap.putBoolean(CHOSEN_ANSWER_CORRECT, chosenAnswerCorrect);
|
||||
dataMap.putBoolean(QUESTION_WAS_ANSWERED, true);
|
||||
PutDataRequest request = putDataMapRequest.asPutDataRequest();
|
||||
request.setUrgent();
|
||||
Wearable.DataApi.putDataItem(mGoogleApiClient, request).await();
|
||||
|
||||
// Remove this question notification.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.wearable.runtimepermissions"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="18"
|
||||
android:targetSdkVersion="23" />
|
||||
|
||||
<!-- Permissions for phone. -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<!-- Permissions for wearable:
|
||||
Earlier watches require their permissions to be a subset of the phone apps permission in order
|
||||
for the wear app to be installed. Therefore, you must include the permissions here as well as in
|
||||
the wear manifest.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.BODY_SENSORS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<application android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@style/Theme.AppCompat.Light">
|
||||
<activity
|
||||
android:name=".MainPhoneActivity"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".PhonePermissionRequestActivity"
|
||||
android:label="@string/title_activity_phone_permission_request"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".WearPermissionRequestActivity"
|
||||
android:label="@string/title_activity_wear_permission_request"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" >
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".IncomingRequestPhoneService"
|
||||
android:enabled="true"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 662 B |
|
After Width: | Height: | Size: 654 B |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".MainActivity"
|
||||
tools:deviceIds="wear_square"
|
||||
android:padding="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/wearBodySensorsPermissionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawableLeft="@drawable/ic_permission_denied"
|
||||
android:text="@string/button_wear_label_activity_main"
|
||||
android:onClick="onClickWearBodySensors" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/phoneStoragePermissionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawableLeft="@drawable/ic_permission_denied"
|
||||
android:text="@string/button_phone_label_activity_main"
|
||||
android:onClick="onClickPhoneStorage" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/output"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="3"
|
||||
android:text="@string/hello_phone_activity_main"
|
||||
android:padding="8dp"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:background="#4c9699">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/no_thanks_activity_phone_permission_request"
|
||||
android:id="@+id/deny_permission_request"
|
||||
android:onClick="onClickDenyPermissionRequest"
|
||||
android:textColor="#ffffff"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/continue_activity_phone_permission_request"
|
||||
android:id="@+id/approve_permission_request"
|
||||
android:onClick="onClickApprovePermissionRequest"
|
||||
android:layout_alignTop="@+id/deny_permission_request"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#ffffff" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="@string/main_message_activity_phone_permission_request"
|
||||
android:id="@+id/mainMessageTextView"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
android:layout_below="@+id/imageView"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="117dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/details_message_activity_phone_permission_request"
|
||||
android:id="@+id/detailsTextView"
|
||||
android:textColor="#ffffff"
|
||||
android:layout_below="@+id/mainMessageTextView"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/imageView"
|
||||
android:src="@drawable/ic_file_folder"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="60dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:background="#4c9699">
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/no_thanks_activity_wear_permission_request"
|
||||
android:id="@+id/deny_permission_request"
|
||||
android:onClick="onClickDenyPermissionRequest"
|
||||
android:textColor="#ffffff"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/continue_activity_wear_permission_request"
|
||||
android:id="@+id/approve_permission_request"
|
||||
android:onClick="onClickApprovePermissionRequest"
|
||||
android:layout_alignTop="@+id/deny_permission_request"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#ffffff" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="@string/main_message_activity_wear_permission_request"
|
||||
android:id="@+id/mainMessageTextView"
|
||||
android:textColor="#ffffff"
|
||||
android:textStyle="bold"
|
||||
android:layout_below="@+id/imageView"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="117dp" />
|
||||
<!--TODO: R.string.dialog_message_activity_main -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:text="@string/details_message_activity_wear_permission_request"
|
||||
android:id="@+id/detailsTextView"
|
||||
android:textColor="#ffffff"
|
||||
android:layout_below="@+id/mainMessageTextView"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/imageView"
|
||||
android:src="@drawable/ic_hardware_watch"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="60dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<!-- Semantic definitions -->
|
||||
|
||||
<dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
|
||||
<dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="Widget.SampleMessage">
|
||||
<item name="android:textAppearance">?android:textAppearanceLarge</item>
|
||||
<item name="android:lineSpacingMultiplier">1.2</item>
|
||||
<item name="android:shadowDy">-6.5</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,22 @@
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<!-- Activity themes -->
|
||||
<style name="Theme.Base" parent="android:Theme.Holo.Light" />
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<!-- Activity themes -->
|
||||
<style name="Theme.Base" parent="android:Theme.Material.Light">
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">RuntimePermissionsWear</string>
|
||||
<string name="intro_message">
|
||||
<![CDATA[
|
||||
|
||||
|
||||
A sample that shows how you can handle remote data that requires permissions both on
|
||||
a wearable device and a mobile device.
|
||||
|
||||
|
||||
]]>
|
||||
</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<string name="hello_phone_activity_main">Happy equals approved, sad equals denied.\n\nTo see results or request permissions, click on the buttons above.</string>
|
||||
<string name="denied_permission_activity_main">You do not have the correct permissions. Tap sad face to bring up permission dialog again.</string>
|
||||
<string name="button_wear_label_activity_main">Wear Sensors</string>
|
||||
<string name="button_phone_label_activity_main">Phone Storage</string>
|
||||
|
||||
<string name="title_activity_phone_permission_request">PhonePermissionRequestActivity</string>
|
||||
<string name="main_message_activity_phone_permission_request">See your directory structure by letting us read your phone\'s storage.</string>
|
||||
<string name="details_message_activity_phone_permission_request">Your phone and watch experience need access to your phone\'s storage to show your top level directories.</string>
|
||||
<string name="no_thanks_activity_phone_permission_request">No Thanks</string>
|
||||
<string name="continue_activity_phone_permission_request">Continue</string>
|
||||
|
||||
<string name="title_activity_wear_permission_request">WearPermissionRequestActivity</string>
|
||||
<string name="main_message_activity_wear_permission_request">See your total sensor count by letting us read your wear\'s sensors.</string>
|
||||
<string name="details_message_activity_wear_permission_request">Your phone and watch experience need access to your wear\'s sensors to show sensor count.</string>
|
||||
<string name="no_thanks_activity_wear_permission_request">No Thanks</string>
|
||||
<string name="continue_activity_wear_permission_request">Open on Watch</string>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
|
||||
|
||||
<dimen name="margin_tiny">4dp</dimen>
|
||||
<dimen name="margin_small">8dp</dimen>
|
||||
<dimen name="margin_medium">16dp</dimen>
|
||||
<dimen name="margin_large">32dp</dimen>
|
||||
<dimen name="margin_huge">64dp</dimen>
|
||||
|
||||
<!-- Semantic definitions -->
|
||||
|
||||
<dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
|
||||
<dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<!-- Activity themes -->
|
||||
|
||||
<style name="Theme.Base" parent="android:Theme.Light" />
|
||||
|
||||
<style name="Theme.Sample" parent="Theme.Base" />
|
||||
|
||||
<style name="AppTheme" parent="Theme.Sample" />
|
||||
<!-- Widget styling -->
|
||||
|
||||
<style name="Widget" />
|
||||
|
||||
<style name="Widget.SampleMessage">
|
||||
<item name="android:textAppearance">?android:textAppearanceMedium</item>
|
||||
<item name="android:lineSpacingMultiplier">1.1</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.SampleMessageTile">
|
||||
<item name="android:background">@drawable/tile</item>
|
||||
<item name="android:shadowColor">#7F000000</item>
|
||||
<item name="android:shadowDy">-3.5</item>
|
||||
<item name="android:shadowRadius">2</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string-array name="android_wear_capabilities">
|
||||
<item>phone_app_runtime_permissions</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.android.wearable.runtimepermissions.common.Constants;
|
||||
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.wearable.DataMap;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
import com.google.android.gms.wearable.WearableListenerService;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Handles all incoming requests for phone data (and permissions) from wear devices.
|
||||
*/
|
||||
public class IncomingRequestPhoneService extends WearableListenerService {
|
||||
|
||||
private static final String TAG = "IncomingRequestService";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d(TAG, "onCreate()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(MessageEvent messageEvent) {
|
||||
super.onMessageReceived(messageEvent);
|
||||
Log.d(TAG, "onMessageReceived(): " + messageEvent);
|
||||
|
||||
String messagePath = messageEvent.getPath();
|
||||
|
||||
if (messagePath.equals(Constants.MESSAGE_PATH_PHONE)) {
|
||||
|
||||
DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
|
||||
int requestType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0);
|
||||
|
||||
if (requestType == Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION) {
|
||||
promptUserForStoragePermission(messageEvent.getSourceNodeId());
|
||||
|
||||
} else if (requestType == Constants.COMM_TYPE_REQUEST_DATA) {
|
||||
respondWithStorageInformation(messageEvent.getSourceNodeId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void promptUserForStoragePermission(String nodeId) {
|
||||
boolean storagePermissionApproved =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (storagePermissionApproved) {
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
|
||||
sendMessage(nodeId, dataMap);
|
||||
} else {
|
||||
// Launch Phone Activity to grant storage permissions.
|
||||
Intent startIntent = new Intent(this, PhonePermissionRequestActivity.class);
|
||||
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
/* This extra is included to alert MainPhoneActivity to send back the permission
|
||||
* results after the user has made their decision in PhonePermissionRequestActivity
|
||||
* and it finishes.
|
||||
*/
|
||||
startIntent.putExtra(MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR, true);
|
||||
startActivity(startIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private void respondWithStorageInformation(String nodeId) {
|
||||
|
||||
boolean storagePermissionApproved =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (!storagePermissionApproved) {
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED);
|
||||
sendMessage(nodeId, dataMap);
|
||||
} else {
|
||||
/* To keep the sample simple, we are only displaying the top level list of directories.
|
||||
* Otherwise, it will return a message that the media wasn't available.
|
||||
*/
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
if (isExternalStorageReadable()) {
|
||||
File externalStorageDirectory = Environment.getExternalStorageDirectory();
|
||||
String[] fileList = externalStorageDirectory.list();
|
||||
|
||||
if (fileList.length > 0) {
|
||||
stringBuilder.append("List of directories on phone:\n");
|
||||
for (String file : fileList) {
|
||||
stringBuilder.append(" - " + file + "\n");
|
||||
}
|
||||
} else {
|
||||
stringBuilder.append("No files in external storage.");
|
||||
}
|
||||
} else {
|
||||
stringBuilder.append("No external media is available.");
|
||||
}
|
||||
|
||||
// Send valid results
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_DATA);
|
||||
dataMap.putString(Constants.KEY_PAYLOAD, stringBuilder.toString());
|
||||
sendMessage(nodeId, dataMap);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(String nodeId, DataMap dataMap) {
|
||||
Log.d(TAG, "sendMessage() Node: " + nodeId);
|
||||
|
||||
GoogleApiClient client = new GoogleApiClient.Builder(this)
|
||||
.addApi(Wearable.API)
|
||||
.build();
|
||||
client.blockingConnect(Constants.CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
|
||||
PendingResult<MessageApi.SendMessageResult> pendingMessageResult =
|
||||
Wearable.MessageApi.sendMessage(
|
||||
client,
|
||||
nodeId,
|
||||
Constants.MESSAGE_PATH_WEAR,
|
||||
dataMap.toByteArray());
|
||||
|
||||
MessageApi.SendMessageResult sendMessageResult =
|
||||
pendingMessageResult.await(
|
||||
Constants.CONNECTION_TIME_OUT_MS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
if (!sendMessageResult.getStatus().isSuccess()) {
|
||||
Log.d(TAG, "Sending message failed, status: "
|
||||
+ sendMessageResult.getStatus());
|
||||
} else {
|
||||
Log.d(TAG, "Message sent successfully");
|
||||
}
|
||||
client.disconnect();
|
||||
}
|
||||
|
||||
private boolean isExternalStorageReadable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
|
||||
return Environment.MEDIA_MOUNTED.equals(state)
|
||||
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.os.Looper;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.example.android.wearable.runtimepermissions.common.Constants;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
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.DataMap;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Displays data that requires runtime permissions both locally (READ_EXTERNAL_STORAGE) and
|
||||
* remotely on wear (BODY_SENSORS).
|
||||
*
|
||||
* The class also handles sending back the results of a permission request from a remote wear device
|
||||
* when the permission has not been approved yet on the phone (uses EXTRA as trigger). In that case,
|
||||
* the IncomingRequestPhoneService launches the splash Activity (PhonePermissionRequestActivity) to
|
||||
* inform user of permission request. After the user decides what to do, it falls back to this
|
||||
* Activity (which has all the GoogleApiClient code) to handle sending data across and keeps user
|
||||
* in app experience.
|
||||
*/
|
||||
public class MainPhoneActivity extends AppCompatActivity implements
|
||||
GoogleApiClient.ConnectionCallbacks,
|
||||
GoogleApiClient.OnConnectionFailedListener,
|
||||
CapabilityApi.CapabilityListener,
|
||||
MessageApi.MessageListener,
|
||||
ResultCallback<MessageApi.SendMessageResult> {
|
||||
|
||||
private static final String TAG = "MainPhoneActivity";
|
||||
|
||||
/*
|
||||
* Alerts Activity that the initial request for permissions came from wear, and the Activity
|
||||
* needs to send back the results (data or permission rejection).
|
||||
*/
|
||||
public static final String EXTRA_PROMPT_PERMISSION_FROM_WEAR =
|
||||
"com.example.android.wearable.runtimepermissions.extra.PROMPT_PERMISSION_FROM_WEAR";
|
||||
|
||||
private static final int REQUEST_WEAR_PERMISSION_RATIONALE = 1;
|
||||
|
||||
private boolean mWearBodySensorsPermissionApproved;
|
||||
private boolean mPhoneStoragePermissionApproved;
|
||||
|
||||
private boolean mWearRequestingPhoneStoragePermission;
|
||||
|
||||
private Button mWearBodySensorsPermissionButton;
|
||||
private Button mPhoneStoragePermissionButton;
|
||||
private TextView mOutputTextView;
|
||||
|
||||
private Set<Node> mWearNodeIds;
|
||||
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.d(TAG, "onCreate()");
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
/*
|
||||
* Since this is a remote permission, we initialize it to false and then check the remote
|
||||
* permission once the GoogleApiClient is connected.
|
||||
*/
|
||||
mWearBodySensorsPermissionApproved = false;
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
// Checks if wear app requested phone permission (permission request opens later if true).
|
||||
mWearRequestingPhoneStoragePermission =
|
||||
getIntent().getBooleanExtra(EXTRA_PROMPT_PERMISSION_FROM_WEAR, false);
|
||||
|
||||
mPhoneStoragePermissionButton =
|
||||
(Button) findViewById(R.id.phoneStoragePermissionButton);
|
||||
|
||||
mWearBodySensorsPermissionButton =
|
||||
(Button) findViewById(R.id.wearBodySensorsPermissionButton);
|
||||
|
||||
mOutputTextView = (TextView) findViewById(R.id.output);
|
||||
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.addApi(Wearable.API)
|
||||
.addConnectionCallbacks(this)
|
||||
.addOnConnectionFailedListener(this)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void onClickWearBodySensors(View view) {
|
||||
|
||||
logToUi("Requested info from wear device(s). New approval may be required.");
|
||||
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE, Constants.COMM_TYPE_REQUEST_DATA);
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
|
||||
public void onClickPhoneStorage(View view) {
|
||||
|
||||
if (mPhoneStoragePermissionApproved) {
|
||||
logToUi(getPhoneStorageInformation());
|
||||
|
||||
} else {
|
||||
// On 23+ (M+) devices, Storage permission not granted. Request permission.
|
||||
Intent startIntent = new Intent(this, PhonePermissionRequestActivity.class);
|
||||
startActivity(startIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Log.d(TAG, "onPause()");
|
||||
super.onPause();
|
||||
if ((mGoogleApiClient != null) && (mGoogleApiClient.isConnected())) {
|
||||
Wearable.CapabilityApi.removeCapabilityListener(
|
||||
mGoogleApiClient,
|
||||
this,
|
||||
Constants.CAPABILITY_WEAR_APP);
|
||||
Wearable.MessageApi.removeListener(mGoogleApiClient, this);
|
||||
mGoogleApiClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
Log.d(TAG, "onResume()");
|
||||
super.onResume();
|
||||
|
||||
/* Enables app to handle 23+ (M+) style permissions. It also covers user changing
|
||||
* permission in settings and coming back to the app.
|
||||
*/
|
||||
mPhoneStoragePermissionApproved =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (mPhoneStoragePermissionApproved) {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (mGoogleApiClient != null) {
|
||||
mGoogleApiClient.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
Log.d(TAG, "onActivityResult()");
|
||||
if (requestCode == REQUEST_WEAR_PERMISSION_RATIONALE) {
|
||||
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
logToUi("Requested permission on wear device(s).");
|
||||
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION);
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(Bundle bundle) {
|
||||
Log.d(TAG, "onConnected()");
|
||||
|
||||
// Set up listeners for capability and message changes.
|
||||
Wearable.CapabilityApi.addCapabilityListener(
|
||||
mGoogleApiClient,
|
||||
this,
|
||||
Constants.CAPABILITY_WEAR_APP);
|
||||
Wearable.MessageApi.addListener(mGoogleApiClient, this);
|
||||
|
||||
// Initial check of capabilities to find the wear nodes.
|
||||
PendingResult<CapabilityApi.GetCapabilityResult> pendingResult =
|
||||
Wearable.CapabilityApi.getCapability(
|
||||
mGoogleApiClient,
|
||||
Constants.CAPABILITY_WEAR_APP,
|
||||
CapabilityApi.FILTER_REACHABLE);
|
||||
|
||||
pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() {
|
||||
@Override
|
||||
public void onResult(CapabilityApi.GetCapabilityResult getCapabilityResult) {
|
||||
|
||||
CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
|
||||
String capabilityName = capabilityInfo.getName();
|
||||
|
||||
boolean wearSupportsSampleApp =
|
||||
capabilityName.equals(Constants.CAPABILITY_WEAR_APP);
|
||||
|
||||
if (wearSupportsSampleApp) {
|
||||
mWearNodeIds = capabilityInfo.getNodes();
|
||||
|
||||
/*
|
||||
* Upon getting all wear nodes, we now need to check if the original request to
|
||||
* launch this activity (and PhonePermissionRequestActivity) was initiated by
|
||||
* a wear device. If it was, we need to send back the permission results (data
|
||||
* or rejection of permission) to the wear device.
|
||||
*
|
||||
* Also, note we set variable to false, this enables the user to continue
|
||||
* changing permissions without sending updates to the wear every time.
|
||||
*/
|
||||
if (mWearRequestingPhoneStoragePermission) {
|
||||
mWearRequestingPhoneStoragePermission = false;
|
||||
sendWearPermissionResults();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int i) {
|
||||
Log.d(TAG, "onConnectionSuspended(): connection to location client suspended");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(ConnectionResult connectionResult) {
|
||||
Log.e(TAG, "onConnectionFailed(): connection to location client failed");
|
||||
}
|
||||
|
||||
|
||||
public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
|
||||
Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo);
|
||||
|
||||
mWearNodeIds = capabilityInfo.getNodes();
|
||||
}
|
||||
|
||||
public void onMessageReceived(MessageEvent messageEvent) {
|
||||
Log.d(TAG, "onMessageReceived(): " + messageEvent);
|
||||
|
||||
String messagePath = messageEvent.getPath();
|
||||
|
||||
if (messagePath.equals(Constants.MESSAGE_PATH_PHONE)) {
|
||||
DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
|
||||
|
||||
int commType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0);
|
||||
|
||||
if (commType == Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED) {
|
||||
mWearBodySensorsPermissionApproved = false;
|
||||
updateWearButtonOnUiThread();
|
||||
|
||||
/* Because our request for remote data requires a remote permission, we now launch
|
||||
* a splash activity informing the user we need those permissions (along with
|
||||
* other helpful information to approve).
|
||||
*/
|
||||
Intent wearPermissionRationale =
|
||||
new Intent(this, WearPermissionRequestActivity.class);
|
||||
startActivityForResult(wearPermissionRationale, REQUEST_WEAR_PERMISSION_RATIONALE);
|
||||
|
||||
} else if (commType == Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION) {
|
||||
mWearBodySensorsPermissionApproved = true;
|
||||
updateWearButtonOnUiThread();
|
||||
logToUi("User approved permission on remote device, requesting data again.");
|
||||
DataMap outgoingDataRequestDataMap = new DataMap();
|
||||
outgoingDataRequestDataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_REQUEST_DATA);
|
||||
sendMessage(outgoingDataRequestDataMap);
|
||||
|
||||
} else if (commType == Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION) {
|
||||
mWearBodySensorsPermissionApproved = false;
|
||||
updateWearButtonOnUiThread();
|
||||
logToUi("User denied permission on remote device.");
|
||||
|
||||
} else if (commType == Constants.COMM_TYPE_RESPONSE_DATA) {
|
||||
mWearBodySensorsPermissionApproved = true;
|
||||
String storageDetails = dataMap.getString(Constants.KEY_PAYLOAD);
|
||||
updateWearButtonOnUiThread();
|
||||
logToUi(storageDetails);
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Unrecognized communication type received.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
|
||||
if (!sendMessageResult.getStatus().isSuccess()) {
|
||||
Log.d(TAG, "Sending message failed, onResult: " + sendMessageResult);
|
||||
updateWearButtonOnUiThread();
|
||||
logToUi("Sending message failed.");
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Message sent.");
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(DataMap dataMap) {
|
||||
Log.d(TAG, "sendMessage(): " + mWearNodeIds);
|
||||
|
||||
if ((mWearNodeIds != null) && (!mWearNodeIds.isEmpty())) {
|
||||
|
||||
PendingResult<MessageApi.SendMessageResult> pendingResult;
|
||||
|
||||
for (Node node : mWearNodeIds) {
|
||||
|
||||
pendingResult = Wearable.MessageApi.sendMessage(
|
||||
mGoogleApiClient,
|
||||
node.getId(),
|
||||
Constants.MESSAGE_PATH_WEAR,
|
||||
dataMap.toByteArray());
|
||||
|
||||
pendingResult.setResultCallback(this, Constants.CONNECTION_TIME_OUT_MS,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
} else {
|
||||
// Unable to retrieve node with proper capability
|
||||
mWearBodySensorsPermissionApproved = false;
|
||||
updateWearButtonOnUiThread();
|
||||
logToUi("Wear devices not available to send message.");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWearButtonOnUiThread() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mWearBodySensorsPermissionApproved) {
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved, 0, 0, 0);
|
||||
} else {
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles all messages for the UI coming on and off the main thread. Not all callbacks happen
|
||||
* on the main thread.
|
||||
*/
|
||||
private void logToUi(final String message) {
|
||||
|
||||
boolean mainUiThread = (Looper.myLooper() == Looper.getMainLooper());
|
||||
|
||||
if (mainUiThread) {
|
||||
|
||||
if (!message.isEmpty()) {
|
||||
Log.d(TAG, message);
|
||||
mOutputTextView.setText(message);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!message.isEmpty()) {
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
Log.d(TAG, message);
|
||||
mOutputTextView.setText(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getPhoneStorageInformation() {
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
String state = Environment.getExternalStorageState();
|
||||
boolean isExternalStorageReadable = Environment.MEDIA_MOUNTED.equals(state)
|
||||
|| Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
|
||||
|
||||
if (isExternalStorageReadable) {
|
||||
File externalStorageDirectory = Environment.getExternalStorageDirectory();
|
||||
String[] fileList = externalStorageDirectory.list();
|
||||
|
||||
if (fileList.length > 0) {
|
||||
|
||||
stringBuilder.append("List of files\n");
|
||||
for (String file : fileList) {
|
||||
stringBuilder.append(" - " + file + "\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
stringBuilder.append("No files in external storage.");
|
||||
}
|
||||
|
||||
} else {
|
||||
stringBuilder.append("No external media is available.");
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private void sendWearPermissionResults() {
|
||||
|
||||
Log.d(TAG, "sendWearPermissionResults()");
|
||||
|
||||
DataMap dataMap = new DataMap();
|
||||
|
||||
if (mPhoneStoragePermissionApproved) {
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
|
||||
} else {
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION);
|
||||
}
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* This is a simple splash screen (activity) for giving more details on why the user should approve
|
||||
* phone permissions for storage. If they choose to move forward, the permission screen
|
||||
* is brought up. Either way (approve or disapprove), this will exit to the MainPhoneActivity after
|
||||
* they are finished with their final decision.
|
||||
*
|
||||
* If this activity is started by our service (IncomingRequestPhoneService) it is marked via an
|
||||
* extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR). That service only starts
|
||||
* this activity if the phone permission hasn't been approved for the data wear is trying to access.
|
||||
* When the user decides within this Activity what to do with the permission request, it closes and
|
||||
* opens the MainPhoneActivity (to maintain the app experience). It also again passes along the same
|
||||
* extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR) to alert MainPhoneActivity to
|
||||
* send the results of the user's decision to the wear device.
|
||||
*/
|
||||
public class PhonePermissionRequestActivity extends AppCompatActivity implements
|
||||
ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
|
||||
private static final String TAG = "PhoneRationale";
|
||||
|
||||
/* Id to identify Location permission request. */
|
||||
private static final int PERMISSION_REQUEST_READ_STORAGE = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// If permissions granted, we start the main activity (shut this activity down).
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
startMainActivity();
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_phone_permission_request);
|
||||
}
|
||||
|
||||
public void onClickApprovePermissionRequest(View view) {
|
||||
Log.d(TAG, "onClickApprovePermissionRequest()");
|
||||
|
||||
// On 23+ (M+) devices, External storage permission not granted. Request permission.
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||
PERMISSION_REQUEST_READ_STORAGE);
|
||||
}
|
||||
|
||||
public void onClickDenyPermissionRequest(View view) {
|
||||
Log.d(TAG, "onClickDenyPermissionRequest()");
|
||||
startMainActivity();
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback received when a permissions request has been completed.
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
|
||||
String permissionResult = "Request code: " + requestCode + ", Permissions: " + permissions
|
||||
+ ", Results: " + grantResults;
|
||||
Log.d(TAG, "onRequestPermissionsResult(): " + permissionResult);
|
||||
|
||||
if (requestCode == PERMISSION_REQUEST_READ_STORAGE) {
|
||||
// Close activity regardless of user's decision (decision picked up in main activity).
|
||||
startMainActivity();
|
||||
}
|
||||
}
|
||||
|
||||
private void startMainActivity() {
|
||||
|
||||
Intent mainActivityIntent = new Intent(this, MainPhoneActivity.class);
|
||||
|
||||
/*
|
||||
* If service started this Activity (b/c wear requested data where permissions were not
|
||||
* approved), tells MainPhoneActivity to send results to wear device (via this extra).
|
||||
*/
|
||||
boolean serviceStartedActivity = getIntent().getBooleanExtra(
|
||||
MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR, false);
|
||||
|
||||
if (serviceStartedActivity) {
|
||||
mainActivityIntent.putExtra(
|
||||
MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR, true);
|
||||
}
|
||||
|
||||
startActivity(mainActivityIntent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* This is a simple splash screen (activity) for giving more details on why the user should approve
|
||||
* phone permissions for storage. If they choose to move forward, the permission screen
|
||||
* is brought up. Either way (approve or disapprove), this will exit to the MainPhoneActivity after
|
||||
* they are finished with their final decision.
|
||||
*
|
||||
* If this activity is started by our service (IncomingRequestPhoneService) it is marked via an
|
||||
* extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR). That service only starts
|
||||
* this activity if the phone permission hasn't been approved for the data wear is trying to access.
|
||||
* When the user decides within this Activity what to do with the permission request, it closes and
|
||||
* opens the MainPhoneActivity (to maintain the app experience). It also again passes along the same
|
||||
* extra (MainPhoneActivity.EXTRA_PROMPT_PERMISSION_FROM_WEAR) to alert MainPhoneActivity to
|
||||
* send the results of the user's decision to the wear device.
|
||||
*/
|
||||
public class WearPermissionRequestActivity extends AppCompatActivity implements
|
||||
ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
|
||||
private static final String TAG = "WearRationale";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_wear_permission_request);
|
||||
}
|
||||
|
||||
public void onClickApprovePermissionRequest(View view) {
|
||||
Log.d(TAG, "onClickApprovePermissionRequest()");
|
||||
setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
public void onClickDenyPermissionRequest(View view) {
|
||||
Log.d(TAG, "onClickDenyPermissionRequest()");
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.wearable.runtimepermissions.common">
|
||||
|
||||
<application android:allowBackup="true" android:label="@string/app_name">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">Shared</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions.common;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A collection of constants that is shared between the wearable and handset apps.
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
// Shared
|
||||
public static final long CONNECTION_TIME_OUT_MS = TimeUnit.SECONDS.toMillis(5);
|
||||
|
||||
public static final String KEY_COMM_TYPE = "communicationType";
|
||||
public static final String KEY_PAYLOAD = "payload";
|
||||
|
||||
// Requests
|
||||
public static final int COMM_TYPE_REQUEST_PROMPT_PERMISSION = 1;
|
||||
public static final int COMM_TYPE_REQUEST_DATA = 2;
|
||||
|
||||
// Responses
|
||||
public static final int COMM_TYPE_RESPONSE_PERMISSION_REQUIRED = 1001;
|
||||
public static final int COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION = 1002;
|
||||
public static final int COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION = 1003;
|
||||
public static final int COMM_TYPE_RESPONSE_DATA = 1004;
|
||||
|
||||
// Phone
|
||||
public static final String CAPABILITY_PHONE_APP = "phone_app_runtime_permissions";
|
||||
public static final String MESSAGE_PATH_PHONE = "/phone_message_path";
|
||||
|
||||
// Wear
|
||||
public static final String CAPABILITY_WEAR_APP = "wear_app_runtime_permissions";
|
||||
public static final String MESSAGE_PATH_WEAR = "/wear_message_path";
|
||||
|
||||
private Constants() {}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.wearable.runtimepermissions" >
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.BODY_SENSORS" />
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="21"
|
||||
android:targetSdkVersion="23" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.DeviceDefault" >
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
|
||||
<!-- If you want your app to run on pre-22, then set required to false -->
|
||||
<uses-library
|
||||
android:name="com.google.android.wearable"
|
||||
android:required="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MainWearActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".RequestPermissionOnPhoneActivity"
|
||||
android:label="@string/title_activity_request_permission_on_phone"
|
||||
android:theme="@android:style/Theme.DeviceDefault.Light">
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".IncomingRequestWearService"
|
||||
android:enabled="true"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 843 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 679 B |
|
After Width: | Height: | Size: 662 B |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 654 B |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 429 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<android.support.wearable.view.WatchViewStub
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/watch_view_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:rectLayout="@layout/rect_activity_main"
|
||||
app:roundLayout="@layout/round_activity_main"
|
||||
tools:context=".MainActivity"
|
||||
tools:deviceIds="wear">
|
||||
</android.support.wearable.view.WatchViewStub>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<android.support.wearable.view.BoxInsetLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:deviceIds="wear"
|
||||
android:background="@color/white"
|
||||
tools:context="com.example.android.wearable.runtimepermissions.RequestPermissionOnPhoneActivity"
|
||||
android:paddingStart="30dp"
|
||||
android:paddingTop="18dp"
|
||||
android:paddingRight="18dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClickPermissionPhoneStorage"
|
||||
android:orientation="vertical"
|
||||
app:layout_box="all">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/permission_message_activity_request_permission_on_phone"
|
||||
android:textSize="16sp"
|
||||
android:paddingRight="6dp"
|
||||
android:textColor="#000000"/>
|
||||
|
||||
<android.support.v4.widget.Space
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<android.support.wearable.view.CircledImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:circle_radius="20dp"
|
||||
app:circle_color="#0086D4"
|
||||
android:src="@drawable/ic_cc_open_on_phone"/>
|
||||
|
||||
<android.support.v4.widget.Space
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/openOnPhone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/open_on_phone_message_activity_request_permission_on_phone"
|
||||
android:textColor="#0086D4"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</android.support.wearable.view.BoxInsetLayout>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".MainActivity"
|
||||
tools:deviceIds="wear_square">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/wearBodySensorsPermissionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawableLeft="@drawable/ic_permission_denied"
|
||||
android:textSize="8sp"
|
||||
android:text="@string/button_wear_label_activity_main"
|
||||
android:onClick="onClickWearBodySensors" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/phoneStoragePermissionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawableLeft="@drawable/ic_permission_denied"
|
||||
android:textSize="8sp"
|
||||
android:text="@string/button_phone_label_activity_main"
|
||||
android:onClick="onClickPhoneStorage" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/output"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="2"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:text="@string/hello_wear_activity_main" />
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".MainActivity"
|
||||
tools:deviceIds="wear_round"
|
||||
android:paddingTop="24dp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/wearBodySensorsPermissionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawableLeft="@drawable/ic_permission_denied"
|
||||
android:textSize="8sp"
|
||||
android:text="@string/button_wear_label_activity_main"
|
||||
android:onClick="onClickWearBodySensors" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/phoneStoragePermissionButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:drawableLeft="@drawable/ic_permission_denied"
|
||||
android:textSize="8sp"
|
||||
android:text="@string/button_phone_label_activity_main"
|
||||
android:onClick="onClickPhoneStorage" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/output"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="2"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:text="@string/hello_wear_activity_main" />
|
||||
|
||||
</LinearLayout>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<dimen name="pair_button_diameter">40dp</dimen>
|
||||
<dimen name="circle_border_normal_width">10dp</dimen>
|
||||
<dimen name="circle_padding">5dp</dimen>
|
||||
<dimen name="circle_radius">35dp</dimen>
|
||||
<dimen name="circle_radius_pressed">40dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">Runtime Permissions</string>
|
||||
|
||||
<string name="hello_wear_activity_main">Happy equals approved, sad equals denied.\n\nTo see results or request permissions, click on the buttons above.</string>
|
||||
<string name="button_wear_label_activity_main">Wear Sensors</string>
|
||||
<string name="button_phone_label_activity_main">Phone Storage</string>
|
||||
|
||||
<string name="title_activity_request_permission_on_phone">PhonePermissionRationale</string>
|
||||
<string name="permission_message_activity_request_permission_on_phone">App requires access to your phone\'s storage.</string>
|
||||
<string name="open_on_phone_message_activity_request_permission_on_phone">Open on phone</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
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.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string-array name="android_wear_capabilities">
|
||||
<item>wear_app_runtime_permissions</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorManager;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.android.wearable.runtimepermissions.common.Constants;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.wearable.CapabilityApi;
|
||||
import com.google.android.gms.wearable.CapabilityInfo;
|
||||
import com.google.android.gms.wearable.DataMap;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
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;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Handles all incoming requests for wear data (and permissions) from phone devices.
|
||||
*/
|
||||
public class IncomingRequestWearService extends WearableListenerService {
|
||||
|
||||
private static final String TAG = "IncomingRequestService";
|
||||
|
||||
public IncomingRequestWearService() {
|
||||
Log.d(TAG, "IncomingRequestWearService()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d(TAG, "onCreate()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(MessageEvent messageEvent) {
|
||||
Log.d(TAG, "onMessageReceived(): " + messageEvent);
|
||||
|
||||
String messagePath = messageEvent.getPath();
|
||||
|
||||
if (messagePath.equals(Constants.MESSAGE_PATH_WEAR)) {
|
||||
DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
|
||||
|
||||
int requestType = dataMap.getInt(Constants.KEY_COMM_TYPE);
|
||||
|
||||
if (requestType == Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION) {
|
||||
promptUserForSensorPermission();
|
||||
|
||||
} else if (requestType == Constants.COMM_TYPE_REQUEST_DATA) {
|
||||
respondWithSensorInformation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void promptUserForSensorPermission() {
|
||||
Log.d(TAG, "promptUserForSensorPermission()");
|
||||
|
||||
boolean sensorPermissionApproved =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (sensorPermissionApproved) {
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
|
||||
sendMessage(dataMap);
|
||||
} else {
|
||||
// Launch Activity to grant sensor permissions.
|
||||
Intent startIntent = new Intent(this, MainWearActivity.class);
|
||||
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startIntent.putExtra(MainWearActivity.EXTRA_PROMPT_PERMISSION_FROM_PHONE, true);
|
||||
startActivity(startIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private void respondWithSensorInformation() {
|
||||
Log.d(TAG, "respondWithSensorInformation()");
|
||||
|
||||
boolean sensorPermissionApproved =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
if (!sensorPermissionApproved) {
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED);
|
||||
sendMessage(dataMap);
|
||||
} else {
|
||||
/* To keep the sample simple, we are only displaying the number of sensors. You could do
|
||||
* something much more complicated.
|
||||
*/
|
||||
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
|
||||
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
|
||||
int numberOfSensorsOnDevice = sensorList.size();
|
||||
|
||||
String sensorSummary = numberOfSensorsOnDevice + " sensors on wear device(s)!";
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_DATA);
|
||||
dataMap.putString(Constants.KEY_PAYLOAD, sensorSummary);
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(DataMap dataMap) {
|
||||
|
||||
Log.d(TAG, "sendMessage(): " + dataMap);
|
||||
|
||||
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
|
||||
.addApi(Wearable.API)
|
||||
.build();
|
||||
ConnectionResult connectionResult =
|
||||
googleApiClient.blockingConnect(
|
||||
Constants.CONNECTION_TIME_OUT_MS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
if (!connectionResult.isSuccess()) {
|
||||
Log.d(TAG, "Google API Client failed to connect.");
|
||||
return;
|
||||
}
|
||||
|
||||
PendingResult<CapabilityApi.GetCapabilityResult> pendingCapabilityResult =
|
||||
Wearable.CapabilityApi.getCapability(
|
||||
googleApiClient,
|
||||
Constants.CAPABILITY_PHONE_APP,
|
||||
CapabilityApi.FILTER_REACHABLE);
|
||||
|
||||
CapabilityApi.GetCapabilityResult getCapabilityResult =
|
||||
pendingCapabilityResult.await(
|
||||
Constants.CONNECTION_TIME_OUT_MS,
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
if (!getCapabilityResult.getStatus().isSuccess()) {
|
||||
Log.d(TAG, "CapabilityApi failed to return any results.");
|
||||
googleApiClient.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
|
||||
String phoneNodeId = pickBestNodeId(capabilityInfo.getNodes());
|
||||
|
||||
PendingResult<MessageApi.SendMessageResult> pendingMessageResult =
|
||||
Wearable.MessageApi.sendMessage(
|
||||
googleApiClient,
|
||||
phoneNodeId,
|
||||
Constants.MESSAGE_PATH_PHONE,
|
||||
dataMap.toByteArray());
|
||||
|
||||
MessageApi.SendMessageResult sendMessageResult =
|
||||
pendingMessageResult.await(Constants.CONNECTION_TIME_OUT_MS, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (!sendMessageResult.getStatus().isSuccess()) {
|
||||
Log.d(TAG, "Sending message failed, onResult: " + sendMessageResult.getStatus());
|
||||
} else {
|
||||
Log.d(TAG, "Message sent successfully");
|
||||
}
|
||||
|
||||
googleApiClient.disconnect();
|
||||
}
|
||||
|
||||
/*
|
||||
* There should only ever be one phone in a node set (much less w/ the correct capability), so
|
||||
* I am just grabbing the first one (which should be the only one).
|
||||
*/
|
||||
private String pickBestNodeId(Set<Node> nodes) {
|
||||
|
||||
Log.d(TAG, "pickBestNodeId: " + nodes);
|
||||
|
||||
|
||||
String bestNodeId = null;
|
||||
/* Find a nearby node or pick one arbitrarily. There should be only one phone connected
|
||||
* that supports this sample.
|
||||
*/
|
||||
for (Node node : nodes) {
|
||||
if (node.isNearby()) {
|
||||
return node.getId();
|
||||
}
|
||||
bestNodeId = node.getId();
|
||||
}
|
||||
return bestNodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.wearable.activity.WearableActivity;
|
||||
import android.support.wearable.view.WatchViewStub;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.example.android.wearable.runtimepermissions.common.Constants;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
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.DataMap;
|
||||
import com.google.android.gms.wearable.MessageApi;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Displays data that requires runtime permissions both locally (BODY_SENSORS) and remotely on
|
||||
* the phone (READ_EXTERNAL_STORAGE).
|
||||
*
|
||||
* The class is also launched by IncomingRequestWearService when the permission for the data the
|
||||
* phone is trying to access hasn't been granted (wear's sensors). If granted in that scenario,
|
||||
* this Activity also sends back the results of the permission request to the phone device (and
|
||||
* the sensor data if approved).
|
||||
*/
|
||||
public class MainWearActivity extends WearableActivity implements
|
||||
GoogleApiClient.ConnectionCallbacks,
|
||||
GoogleApiClient.OnConnectionFailedListener,
|
||||
CapabilityApi.CapabilityListener,
|
||||
MessageApi.MessageListener,
|
||||
ActivityCompat.OnRequestPermissionsResultCallback {
|
||||
|
||||
private static final String TAG = "MainWearActivity";
|
||||
|
||||
/* Id to identify local permission request for body sensors. */
|
||||
private static final int PERMISSION_REQUEST_READ_BODY_SENSORS = 1;
|
||||
|
||||
/* Id to identify starting/closing RequestPermissionOnPhoneActivity (startActivityForResult). */
|
||||
private static final int REQUEST_PHONE_PERMISSION = 1;
|
||||
|
||||
public static final String EXTRA_PROMPT_PERMISSION_FROM_PHONE =
|
||||
"com.example.android.wearable.runtimepermissions.extra.PROMPT_PERMISSION_FROM_PHONE";
|
||||
|
||||
private boolean mWearBodySensorsPermissionApproved;
|
||||
private boolean mPhoneStoragePermissionApproved;
|
||||
|
||||
private boolean mPhoneRequestingWearSensorPermission;
|
||||
|
||||
private Button mWearBodySensorsPermissionButton;
|
||||
private Button mPhoneStoragePermissionButton;
|
||||
private TextView mOutputTextView;
|
||||
|
||||
private String mPhoneNodeId;
|
||||
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.d(TAG, "onCreate()");
|
||||
super.onCreate(savedInstanceState);;
|
||||
|
||||
/*
|
||||
* Since this is a remote permission, we initialize it to false and then check the remote
|
||||
* permission once the GoogleApiClient is connected.
|
||||
*/
|
||||
mPhoneStoragePermissionApproved = false;
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
setAmbientEnabled();
|
||||
|
||||
// Checks if phone app requested wear permission (permission request opens later if true).
|
||||
mPhoneRequestingWearSensorPermission =
|
||||
getIntent().getBooleanExtra(EXTRA_PROMPT_PERMISSION_FROM_PHONE, false);
|
||||
|
||||
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
|
||||
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
|
||||
@Override
|
||||
public void onLayoutInflated(WatchViewStub stub) {
|
||||
|
||||
mWearBodySensorsPermissionButton =
|
||||
(Button) stub.findViewById(R.id.wearBodySensorsPermissionButton);
|
||||
|
||||
if (mWearBodySensorsPermissionApproved) {
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved, 0, 0, 0);
|
||||
}
|
||||
|
||||
mPhoneStoragePermissionButton =
|
||||
(Button) stub.findViewById(R.id.phoneStoragePermissionButton);
|
||||
|
||||
mOutputTextView = (TextView) stub.findViewById(R.id.output);
|
||||
|
||||
if (mPhoneRequestingWearSensorPermission) {
|
||||
launchPermissionDialogForPhone();
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.addApi(Wearable.API)
|
||||
.addConnectionCallbacks(this)
|
||||
.addOnConnectionFailedListener(this)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void onClickWearBodySensors(View view) {
|
||||
|
||||
if (mWearBodySensorsPermissionApproved) {
|
||||
|
||||
// To keep the sample simple, we are only displaying the number of sensors.
|
||||
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
|
||||
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
|
||||
int numberOfSensorsOnDevice = sensorList.size();
|
||||
|
||||
logToUi(numberOfSensorsOnDevice + " sensors on device(s)!");
|
||||
|
||||
} else {
|
||||
logToUi("Requested local permission.");
|
||||
// On 23+ (M+) devices, GPS permission not granted. Request permission.
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
new String[]{Manifest.permission.BODY_SENSORS},
|
||||
PERMISSION_REQUEST_READ_BODY_SENSORS);
|
||||
}
|
||||
}
|
||||
|
||||
public void onClickPhoneStorage(View view) {
|
||||
|
||||
logToUi("Requested info from phone. New approval may be required.");
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_REQUEST_DATA);
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Log.d(TAG, "onPause()");
|
||||
super.onPause();
|
||||
if ((mGoogleApiClient != null) && mGoogleApiClient.isConnected()) {
|
||||
Wearable.CapabilityApi.removeCapabilityListener(
|
||||
mGoogleApiClient,
|
||||
this,
|
||||
Constants.CAPABILITY_PHONE_APP);
|
||||
Wearable.MessageApi.removeListener(mGoogleApiClient, this);
|
||||
mGoogleApiClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
Log.d(TAG, "onResume()");
|
||||
super.onResume();
|
||||
if (mGoogleApiClient != null) {
|
||||
mGoogleApiClient.connect();
|
||||
}
|
||||
|
||||
// Enables app to handle 23+ (M+) style permissions.
|
||||
mWearBodySensorsPermissionApproved =
|
||||
ActivityCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Because this wear activity is marked "android:launchMode='singleInstance'" in the manifest,
|
||||
* we need to allow the permissions dialog to be opened up from the phone even if the wear app
|
||||
* is in the foreground. By overriding onNewIntent, we can cover that use case.
|
||||
*/
|
||||
@Override
|
||||
protected void onNewIntent (Intent intent) {
|
||||
Log.d(TAG, "onNewIntent()");
|
||||
super.onNewIntent(intent);
|
||||
|
||||
// Checks if phone app requested wear permissions (opens up permission request if true).
|
||||
mPhoneRequestingWearSensorPermission =
|
||||
intent.getBooleanExtra(EXTRA_PROMPT_PERMISSION_FROM_PHONE, false);
|
||||
|
||||
if (mPhoneRequestingWearSensorPermission) {
|
||||
launchPermissionDialogForPhone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnterAmbient(Bundle ambientDetails) {
|
||||
Log.d(TAG, "onEnterAmbient() " + ambientDetails);
|
||||
|
||||
if (mWearBodySensorsPermissionApproved) {
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved_bw, 0, 0, 0);
|
||||
} else {
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied_bw, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (mPhoneStoragePermissionApproved) {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved_bw, 0, 0, 0);
|
||||
} else {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied_bw, 0, 0, 0);
|
||||
}
|
||||
super.onEnterAmbient(ambientDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExitAmbient() {
|
||||
Log.d(TAG, "onExitAmbient()");
|
||||
|
||||
if (mWearBodySensorsPermissionApproved) {
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved, 0, 0, 0);
|
||||
} else {
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (mPhoneStoragePermissionApproved) {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved, 0, 0, 0);
|
||||
} else {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied, 0, 0, 0);
|
||||
}
|
||||
super.onExitAmbient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(Bundle bundle) {
|
||||
Log.d(TAG, "onConnected()");
|
||||
|
||||
// Set up listeners for capability and message changes.
|
||||
Wearable.CapabilityApi.addCapabilityListener(
|
||||
mGoogleApiClient,
|
||||
this,
|
||||
Constants.CAPABILITY_PHONE_APP);
|
||||
Wearable.MessageApi.addListener(mGoogleApiClient, this);
|
||||
|
||||
// Initial check of capabilities to find the phone.
|
||||
PendingResult<CapabilityApi.GetCapabilityResult> pendingResult =
|
||||
Wearable.CapabilityApi.getCapability(
|
||||
mGoogleApiClient,
|
||||
Constants.CAPABILITY_PHONE_APP,
|
||||
CapabilityApi.FILTER_REACHABLE);
|
||||
|
||||
pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() {
|
||||
@Override
|
||||
public void onResult(CapabilityApi.GetCapabilityResult getCapabilityResult) {
|
||||
|
||||
if (getCapabilityResult.getStatus().isSuccess()) {
|
||||
CapabilityInfo capabilityInfo = getCapabilityResult.getCapability();
|
||||
mPhoneNodeId = pickBestNodeId(capabilityInfo.getNodes());
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Failed CapabilityApi result: "
|
||||
+ getCapabilityResult.getStatus());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int i) {
|
||||
Log.d(TAG, "onConnectionSuspended(): connection to location client suspended");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(ConnectionResult connectionResult) {
|
||||
Log.e(TAG, "onConnectionFailed(): connection to location client failed");
|
||||
}
|
||||
|
||||
public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
|
||||
Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo);
|
||||
|
||||
mPhoneNodeId = pickBestNodeId(capabilityInfo.getNodes());
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback received when a permissions request has been completed.
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
|
||||
String permissionResult = "Request code: " + requestCode + ", Permissions: " + permissions
|
||||
+ ", Results: " + grantResults;
|
||||
Log.d(TAG, "onRequestPermissionsResult(): " + permissionResult);
|
||||
|
||||
|
||||
if (requestCode == PERMISSION_REQUEST_READ_BODY_SENSORS) {
|
||||
|
||||
if ((grantResults.length == 1)
|
||||
&& (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
||||
|
||||
mWearBodySensorsPermissionApproved = true;
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved, 0, 0, 0);
|
||||
|
||||
// To keep the sample simple, we are only displaying the number of sensors.
|
||||
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
|
||||
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
|
||||
int numberOfSensorsOnDevice = sensorList.size();
|
||||
|
||||
String sensorSummary = numberOfSensorsOnDevice + " sensors on this device!";
|
||||
logToUi(sensorSummary);
|
||||
|
||||
if (mPhoneRequestingWearSensorPermission) {
|
||||
// Resets so this isn't triggered every time permission is changed in app.
|
||||
mPhoneRequestingWearSensorPermission = false;
|
||||
|
||||
// Send 'approved' message to remote phone since it started Activity.
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION);
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
mWearBodySensorsPermissionApproved = false;
|
||||
mWearBodySensorsPermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied, 0, 0, 0);
|
||||
|
||||
if (mPhoneRequestingWearSensorPermission) {
|
||||
// Resets so this isn't triggered every time permission is changed in app.
|
||||
mPhoneRequestingWearSensorPermission = false;
|
||||
// Send 'denied' message to remote phone since it started Activity.
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION);
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onMessageReceived(MessageEvent messageEvent) {
|
||||
Log.d(TAG, "onMessageReceived(): " + messageEvent);
|
||||
|
||||
String messagePath = messageEvent.getPath();
|
||||
|
||||
if (messagePath.equals(Constants.MESSAGE_PATH_WEAR)) {
|
||||
|
||||
DataMap dataMap = DataMap.fromByteArray(messageEvent.getData());
|
||||
int commType = dataMap.getInt(Constants.KEY_COMM_TYPE, 0);
|
||||
|
||||
if (commType == Constants.COMM_TYPE_RESPONSE_PERMISSION_REQUIRED) {
|
||||
mPhoneStoragePermissionApproved = false;
|
||||
updatePhoneButtonOnUiThread();
|
||||
|
||||
/* Because our request for remote data requires a remote permission, we now launch
|
||||
* a splash activity informing the user we need those permissions (along with
|
||||
* other helpful information to approve).
|
||||
*/
|
||||
Intent phonePermissionRationaleIntent =
|
||||
new Intent(this, RequestPermissionOnPhoneActivity.class);
|
||||
startActivityForResult(phonePermissionRationaleIntent, REQUEST_PHONE_PERMISSION);
|
||||
|
||||
} else if (commType == Constants.COMM_TYPE_RESPONSE_USER_APPROVED_PERMISSION) {
|
||||
mPhoneStoragePermissionApproved = true;
|
||||
updatePhoneButtonOnUiThread();
|
||||
logToUi("User approved permission on remote device, requesting data again.");
|
||||
DataMap outgoingDataRequestDataMap = new DataMap();
|
||||
outgoingDataRequestDataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_REQUEST_DATA);
|
||||
sendMessage(outgoingDataRequestDataMap);
|
||||
|
||||
} else if (commType == Constants.COMM_TYPE_RESPONSE_USER_DENIED_PERMISSION) {
|
||||
mPhoneStoragePermissionApproved = false;
|
||||
updatePhoneButtonOnUiThread();
|
||||
logToUi("User denied permission on remote device.");
|
||||
|
||||
} else if (commType == Constants.COMM_TYPE_RESPONSE_DATA) {
|
||||
mPhoneStoragePermissionApproved = true;
|
||||
String storageDetails = dataMap.getString(Constants.KEY_PAYLOAD);
|
||||
updatePhoneButtonOnUiThread();
|
||||
logToUi(storageDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(DataMap dataMap) {
|
||||
Log.d(TAG, "sendMessage(): " + mPhoneNodeId);
|
||||
|
||||
if (mPhoneNodeId != null) {
|
||||
|
||||
PendingResult<MessageApi.SendMessageResult> pendingResult =
|
||||
Wearable.MessageApi.sendMessage(
|
||||
mGoogleApiClient,
|
||||
mPhoneNodeId,
|
||||
Constants.MESSAGE_PATH_PHONE,
|
||||
dataMap.toByteArray());
|
||||
|
||||
pendingResult.setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
|
||||
@Override
|
||||
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
|
||||
|
||||
if (!sendMessageResult.getStatus().isSuccess()) {
|
||||
updatePhoneButtonOnUiThread();
|
||||
logToUi("Sending message failed.");
|
||||
|
||||
} else {
|
||||
Log.d(TAG, "Message sent successfully.");
|
||||
}
|
||||
}
|
||||
}, Constants.CONNECTION_TIME_OUT_MS, TimeUnit.SECONDS);
|
||||
|
||||
} else {
|
||||
// Unable to retrieve node with proper capability
|
||||
mPhoneStoragePermissionApproved = false;
|
||||
updatePhoneButtonOnUiThread();
|
||||
logToUi("Phone not available to send message.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
// Check which request we're responding to
|
||||
if (requestCode == REQUEST_PHONE_PERMISSION) {
|
||||
// Make sure the request was successful
|
||||
if (resultCode == RESULT_OK) {
|
||||
logToUi("Requested permission on phone.");
|
||||
DataMap dataMap = new DataMap();
|
||||
dataMap.putInt(Constants.KEY_COMM_TYPE,
|
||||
Constants.COMM_TYPE_REQUEST_PROMPT_PERMISSION);
|
||||
sendMessage(dataMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There should only ever be one phone in a node set (much less w/ the correct capability), so
|
||||
* I am just grabbing the first one (which should be the only one).
|
||||
*/
|
||||
private String pickBestNodeId(Set<Node> nodes) {
|
||||
|
||||
String bestNodeId = null;
|
||||
// Find a nearby node or pick one arbitrarily.
|
||||
for (Node node : nodes) {
|
||||
if (node.isNearby()) {
|
||||
return node.getId();
|
||||
}
|
||||
bestNodeId = node.getId();
|
||||
}
|
||||
return bestNodeId;
|
||||
}
|
||||
|
||||
/*
|
||||
* If Phone triggered the wear app for permissions, we open up the permission
|
||||
* dialog after inflation.
|
||||
*/
|
||||
private void launchPermissionDialogForPhone() {
|
||||
Log.d(TAG, "launchPermissionDialogForPhone()");
|
||||
|
||||
if (!mWearBodySensorsPermissionApproved) {
|
||||
// On 23+ (M+) devices, GPS permission not granted. Request permission.
|
||||
ActivityCompat.requestPermissions(
|
||||
MainWearActivity.this,
|
||||
new String[]{Manifest.permission.BODY_SENSORS},
|
||||
PERMISSION_REQUEST_READ_BODY_SENSORS);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePhoneButtonOnUiThread() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (mPhoneStoragePermissionApproved) {
|
||||
|
||||
if (isAmbient()) {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved_bw, 0, 0, 0);
|
||||
} else {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_approved, 0, 0, 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (isAmbient()) {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied_bw, 0, 0, 0);
|
||||
} else {
|
||||
mPhoneStoragePermissionButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_permission_denied, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles all messages for the UI coming on and off the main thread. Not all callbacks happen
|
||||
* on the main thread.
|
||||
*/
|
||||
private void logToUi(final String message) {
|
||||
|
||||
boolean mainUiThread = (Looper.myLooper() == Looper.getMainLooper());
|
||||
|
||||
if (mainUiThread) {
|
||||
|
||||
if (!message.isEmpty()) {
|
||||
Log.d(TAG, message);
|
||||
mOutputTextView.setText(message);
|
||||
}
|
||||
|
||||
} else {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!message.isEmpty()) {
|
||||
Log.d(TAG, message);
|
||||
mOutputTextView.setText(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Google Inc. All Rights Reserved.
|
||||
*
|
||||
* 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.runtimepermissions;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.wearable.activity.WearableActivity;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Asks user if they want to open permission screen on their remote device (phone).
|
||||
*/
|
||||
public class RequestPermissionOnPhoneActivity extends WearableActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_request_permission_on_phone);
|
||||
setAmbientEnabled();
|
||||
}
|
||||
|
||||
public void onClickPermissionPhoneStorage(View view) {
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
11
samples/browseable/RuntimePermissionsWear/_index.jd
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
page.tags="RuntimePermissionsWear"
|
||||
sample.group=Wearable
|
||||
@jd:body
|
||||
|
||||
<p>
|
||||
|
||||
A sample that shows how you can handle remote data that requires permissions both on
|
||||
a wearable device and a mobile device.
|
||||
|
||||
</p>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent" android:layout_height="match_parent">
|
||||
<View
|
||||
android:id="@+id/center"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_centerInParent="true"/>
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_above="@id/center"
|
||||
android:layout_marginBottom="18dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/start_saving_gps"/>
|
||||
<android.support.wearable.view.CircledImageView
|
||||
android:id="@+id/cancelBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_below="@id/center"
|
||||
android:layout_toLeftOf="@id/center"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@drawable/ic_cancel_80"
|
||||
app:circle_color="@color/grey"
|
||||
android:onClick="onClick"
|
||||
app:circle_padding="@dimen/circle_padding"
|
||||
app:circle_radius="@dimen/circle_radius"
|
||||
app:circle_radius_pressed="@dimen/circle_radius_pressed" />
|
||||
<android.support.wearable.view.CircledImageView
|
||||
android:id="@+id/submitBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_below="@id/center"
|
||||
android:layout_toRightOf="@id/center"
|
||||
android:layout_marginStart="10dp"
|
||||
android:src="@drawable/ic_confirmation_80"
|
||||
app:circle_color="@color/blue"
|
||||
android:onClick="onClick"
|
||||
app:circle_padding="@dimen/circle_padding"
|
||||
app:circle_radius="@dimen/circle_radius"
|
||||
app:circle_radius_pressed="@dimen/circle_radius_pressed" />
|
||||
</RelativeLayout>
|
||||
@@ -408,6 +408,7 @@ public class WearableMainActivity extends WearableActivity implements
|
||||
putDataMapRequest.getDataMap()
|
||||
.putLong(Constants.KEY_TIME, entry.calendar.getTimeInMillis());
|
||||
PutDataRequest request = putDataMapRequest.asPutDataRequest();
|
||||
request.setUrgent();
|
||||
Wearable.DataApi.putDataItem(mGoogleApiClient, request)
|
||||
.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
|
||||
@Override
|
||||
|
||||