diff options
author | Adam Koch <akoch@google.com> | 2015-09-22 06:03:46 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-09-22 06:03:46 +0000 |
commit | 1d1948e9152433a491780f085daded25f0b4ec3c (patch) | |
tree | cd3bb170261841769ae1b4073a3bb0713d7e2a7e | |
parent | 8799315b3b67c173c3903ceccf0abe0a4b1dc400 (diff) | |
parent | ab673fa191558ba46553ea446887c2490e5b54e2 (diff) | |
download | android-1d1948e9152433a491780f085daded25f0b4ec3c.tar.gz |
Merge "Update XYZTouristAttractions for M permissions + minor bug fixes" into mnc-docs
12 files changed, 141 insertions, 32 deletions
diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml b/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml index 76f0198d..9d88b398 100644 --- a/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml +++ b/wearable/wear/XYZTouristAttractions/Application/src/main/AndroidManifest.xml @@ -16,17 +16,21 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.android.xyztouristattractions" > + xmlns:tools="http://schemas.android.com/tools" + package="com.example.android.xyztouristattractions"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:theme="@style/XYZAppTheme" > + android:theme="@style/XYZAppTheme" + android:fullBackupContent="true"> <activity android:name=".ui.AttractionListActivity" diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java index 50be3626..62ddbf9a 100644 --- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java +++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/provider/TouristAttractions.java @@ -18,7 +18,6 @@ package com.example.android.xyztouristattractions.provider; import android.net.Uri; -import com.example.android.xyztouristattractions.BuildConfig; import com.example.android.xyztouristattractions.common.Attraction; import com.google.android.gms.location.Geofence; import com.google.android.gms.maps.model.LatLng; @@ -126,11 +125,8 @@ public class TouristAttractions { public static String getClosestCity(LatLng curLatLng) { if (curLatLng == null) { - // In debug build still return a city so some data is displayed - if (BuildConfig.DEBUG) { - return TEST_CITY; - } - return null; + // If location is unknown return test city so some data is shown + return TEST_CITY; } double minDistance = 0; diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java index 4112a656..48bad2c6 100644 --- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java +++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/service/UtilityService.java @@ -150,6 +150,11 @@ public class UtilityService extends IntentService { */ private void addGeofencesInternal() { Log.v(TAG, ACTION_ADD_GEOFENCES); + + if (!Utils.checkFineLocationPermission(this)) { + return; + } + GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .build(); @@ -202,6 +207,11 @@ public class UtilityService extends IntentService { */ private void requestLocationInternal() { Log.v(TAG, ACTION_REQUEST_LOCATION); + + if (!Utils.checkFineLocationPermission(this)) { + return; + } + GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .build(); @@ -358,7 +368,7 @@ public class UtilityService extends IntentService { .setSmallIcon(R.drawable.ic_stat_maps_pin_drop) .setContentIntent(pendingIntent) .setDeleteIntent(deletePendingIntent) - .setColor(getResources().getColor(R.color.colorPrimary)) + .setColor(getResources().getColor(R.color.colorPrimary, getTheme())) .setCategory(Notification.CATEGORY_RECOMMENDATION) .setAutoCancel(true); diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java index 8d2908c2..8c23f3dd 100644 --- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java +++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListActivity.java @@ -16,11 +16,16 @@ package com.example.android.xyztouristattractions.ui; +import android.Manifest; +import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.design.widget.Snackbar; +import android.support.v4.app.ActivityCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.widget.Toast; import com.example.android.xyztouristattractions.R; @@ -31,7 +36,10 @@ import com.example.android.xyztouristattractions.service.UtilityService; * The main tourist attraction activity screen which contains a list of * attractions sorted by distance. */ -public class AttractionListActivity extends AppCompatActivity { +public class AttractionListActivity extends AppCompatActivity implements + ActivityCompat.OnRequestPermissionsResultCallback { + + private static final int PERMISSION_REQ = 0; @Override protected void onCreate(Bundle savedInstanceState) { @@ -44,7 +52,23 @@ public class AttractionListActivity extends AppCompatActivity { .commit(); } - UtilityService.addGeofences(this); + // Check fine location permission has been granted + if (!Utils.checkFineLocationPermission(this)) { + // See if user has denied permission in the past + if (ActivityCompat.shouldShowRequestPermissionRationale( + this, Manifest.permission.ACCESS_FINE_LOCATION)) { + // Show a simple snackbar explaining the request instead + showPermissionSnackbar(); + } else { + // Otherwise request permission from user + if (savedInstanceState == null) { + requestFineLocationPermission(); + } + } + } else { + // Otherwise permission is granted (which is always the case on pre-M devices) + fineLocationPermissionGranted(); + } } @Override @@ -88,6 +112,51 @@ public class AttractionListActivity extends AppCompatActivity { } /** + * Permissions request result callback + */ + @Override + public void onRequestPermissionsResult( + int requestCode, String[] permissions, int[] grantResults) { + switch (requestCode) { + case PERMISSION_REQ: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + fineLocationPermissionGranted(); + } + } + } + + /** + * Request the fine location permission from the user + */ + private void requestFineLocationPermission() { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQ); + } + + /** + * Run when fine location permission has been granted + */ + private void fineLocationPermissionGranted() { + UtilityService.addGeofences(this); + UtilityService.requestLocation(this); + } + + /** + * Show a permission explanation snackbar + */ + private void showPermissionSnackbar() { + Snackbar.make( + findViewById(R.id.container), R.string.permission_explanation, Snackbar.LENGTH_LONG) + .setAction(R.string.permission_explanation_action, new View.OnClickListener() { + @Override + public void onClick(View v) { + requestFineLocationPermission(); + } + }) + .show(); + } + + /** * Show a basic debug dialog to provide more info on the built-in debug * options. */ diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java index e86ac3f9..71439b14 100644 --- a/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java +++ b/wearable/wear/XYZTouristAttractions/Application/src/main/java/com/example/android/xyztouristattractions/ui/AttractionListFragment.java @@ -59,6 +59,7 @@ public class AttractionListFragment extends Fragment { private AttractionAdapter mAdapter; private LatLng mLatestLocation; private int mImageSize; + private boolean mItemClicked; public AttractionListFragment() {} @@ -86,6 +87,7 @@ public class AttractionListFragment extends Fragment { @Override public void onResume() { super.onResume(); + mItemClicked = false; LocalBroadcastManager.getInstance(getActivity()).registerReceiver( mBroadcastReceiver, UtilityService.getLocationUpdatedIntentFilter()); } @@ -186,9 +188,12 @@ public class AttractionListFragment extends Fragment { @Override public void onItemClick(View view, int position) { - View heroView = view.findViewById(android.R.id.icon); - DetailActivity.launch( - getActivity(), mAdapter.mAttractionList.get(position).name, heroView); + if (!mItemClicked) { + mItemClicked = true; + View heroView = view.findViewById(android.R.id.icon); + DetailActivity.launch( + getActivity(), mAdapter.mAttractionList.get(position).name, heroView); + } } } diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml b/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml index 86616102..17c9f66e 100644 --- a/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml +++ b/wearable/wear/XYZTouristAttractions/Application/src/main/res/layout/activity_main.xml @@ -14,12 +14,13 @@ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<android.support.design.widget.CoordinatorLayout + + xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" tools:context=".MainActivity"> -</LinearLayout>
\ No newline at end of file +</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml b/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml index 5f3ee145..fede01e8 100644 --- a/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml +++ b/wearable/wear/XYZTouristAttractions/Application/src/main/res/values/strings.xml @@ -45,5 +45,7 @@ appear further down your stream </string> <string name="action_test_toggle_geofence">Toggle Geofence Trigger</string> <string name="action_map">Show on Map</string> + <string name="permission_explanation">Allow this app to use your location to show distance to attractions?</string> + <string name="permission_explanation_action">Let\'s do it!</string> </resources> diff --git a/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java b/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java index 70e05bf2..6fa51299 100644 --- a/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java +++ b/wearable/wear/XYZTouristAttractions/Shared/src/main/java/com/example/android/xyztouristattractions/common/Utils.java @@ -16,13 +16,16 @@ package com.example.android.xyztouristattractions.common; +import android.Manifest; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Point; import android.graphics.Rect; import android.preference.PreferenceManager; +import android.support.v4.content.ContextCompat; import android.util.Log; import android.view.Display; @@ -54,6 +57,15 @@ public class Utils { private static final String DISTANCE_M_POSTFIX = "m"; /** + * Check if the app has access to fine location permission. On pre-M + * devices this will always return true. + */ + public static boolean checkFineLocationPermission(Context context) { + return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission( + context, Manifest.permission.ACCESS_FINE_LOCATION); + } + + /** * Calculate distance between two LatLng points and format it nicely for * display. As this is a sample, it only statically supports metric units. * A production app should check locale and support the correct units. @@ -90,6 +102,10 @@ public class Utils { * Fetch the location from app preferences. */ public static LatLng getLocation(Context context) { + if (!checkFineLocationPermission(context)) { + return null; + } + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); Long lat = prefs.getLong(PREFERENCES_LAT, Long.MAX_VALUE); Long lng = prefs.getLong(PREFERENCES_LNG, Long.MAX_VALUE); diff --git a/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml b/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml index 80d0c920..d353b290 100644 --- a/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml +++ b/wearable/wear/XYZTouristAttractions/Wearable/src/main/AndroidManifest.xml @@ -16,10 +16,14 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.android.xyztouristattractions" > + xmlns:tools="http://schemas.android.com/tools" + package="com.example.android.xyztouristattractions"> <uses-feature android:name="android.hardware.type.watch" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" /> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="22" /> diff --git a/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java b/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java index d2282511..ea071f05 100644 --- a/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java +++ b/wearable/wear/XYZTouristAttractions/Wearable/src/main/java/com/example/android/xyztouristattractions/service/ListenerService.java @@ -17,11 +17,12 @@ package com.example.android.xyztouristattractions.service; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; import android.util.Log; import com.example.android.xyztouristattractions.R; @@ -39,7 +40,6 @@ import com.google.android.gms.wearable.Wearable; import com.google.android.gms.wearable.WearableListenerService; import java.util.ArrayList; -import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -106,21 +106,20 @@ public class ListenerService extends WearableListenerService { PendingIntent deletePendingIntent = PendingIntent.getService( this, 0, UtilityService.getClearRemoteNotificationsIntent(this), 0); - Notification notification = new Notification.Builder(this) + Notification notification = new NotificationCompat.Builder(this) .setContentText(getResources().getQuantityString( R.plurals.attractions_found, count, count)) .setSmallIcon(R.mipmap.ic_launcher) .setDeleteIntent(deletePendingIntent) - .addAction(R.drawable.ic_full_explore, + .addAction(new NotificationCompat.Action.Builder(R.drawable.ic_full_explore, getString(R.string.action_explore), - pendingIntent) - .extend(new Notification.WearableExtender() + pendingIntent).build()) + .extend(new NotificationCompat.WearableExtender() .setBackground(bitmap) ) .build(); - NotificationManager notificationManager = - (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); notificationManager.notify(Constants.WEAR_NOTIFICATION_ID, notification); googleApiClient.disconnect(); diff --git a/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml b/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml index ac01509f..45495ad6 100644 --- a/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml +++ b/wearable/wear/XYZTouristAttractions/Wearable/src/main/res/layout/gridpager_action.xml @@ -26,4 +26,4 @@ android:text="@string/action_open" android:maxLines="1" android:color="@color/colorPrimary" - app:rippleColor="@color/colorAccent" /> + app:buttonRippleColor="@color/colorAccent" /> diff --git a/wearable/wear/XYZTouristAttractions/template-params.xml b/wearable/wear/XYZTouristAttractions/template-params.xml index cfa368f9..473b1110 100644 --- a/wearable/wear/XYZTouristAttractions/template-params.xml +++ b/wearable/wear/XYZTouristAttractions/template-params.xml @@ -19,7 +19,7 @@ <group>Wearable</group> <package>com.example.android.xyztouristattractions</package> <minSdk>18</minSdk> - <targetSdkVersion>22</targetSdkVersion> + <targetSdkVersion>23</targetSdkVersion> <wearable> <has_handheld_app>true</has_handheld_app> @@ -27,11 +27,12 @@ <dependency>com.google.android.gms:play-services-location</dependency> <dependency>com.google.maps.android:android-maps-utils:0.3.4</dependency> - <dependency>com.github.bumptech.glide:glide:3.6.0</dependency> - <dependency>com.android.support:appcompat-v7:22.2.0</dependency> - <dependency>com.android.support:recyclerview-v7:22.2.0</dependency> - <dependency>com.android.support:design:22.2.0</dependency> + <dependency>com.github.bumptech.glide:glide:3.6.1</dependency> + <dependency>com.android.support:appcompat-v7:23.0.0</dependency> + <dependency>com.android.support:recyclerview-v7:23.0.0</dependency> + <dependency>com.android.support:design:23.0.0</dependency> <dependency_wearable>com.google.android.gms:play-services-location</dependency_wearable> + <dependency_shared>com.android.support:support-v13:23.0.0</dependency_shared> <dependency_shared>com.google.android.gms:play-services-wearable</dependency_shared> <dependency_shared>com.google.android.gms:play-services-location</dependency_shared> <dependency_shared>com.google.maps.android:android-maps-utils:0.3.4</dependency_shared> @@ -83,6 +84,8 @@ attractions in a GridViewPager UI component. <android>android.support.v7.appcompat</android> <android>android.support.v7.widget.RecyclerView</android> <android>android.support.v7.widget.GridLayoutManager</android> + <android>android.support.v4.content.ContextCompat</android> + <android>android.support.v4.app.ActivityCompat</android> <android>android.support.design.widget.FloatingActionButton</android> <android>android.support.design.widget.CoordinatorLayout</android> <android>com.google.android.gms.common.api.GoogleApiClient</android> |