aboutsummaryrefslogtreecommitdiff
path: root/system
diff options
context:
space:
mode:
authorJan-Felix Schmakeit <jfschmakeit@google.com>2015-08-04 17:25:03 +1000
committerJan-Felix Schmakeit <jfschmakeit@google.com>2015-08-17 18:51:37 +1000
commit02aad741ef92ddc9759575733202c15258b5a616 (patch)
tree83485a908f0934ad4f86a44a2b8002cf93bbd995 /system
parent6ab42c36e877ca920212b0a1586268a2e855c2e3 (diff)
downloadandroid-02aad741ef92ddc9759575733202c15258b5a616.tar.gz
Update RuntimePermissions sample for API 23.
Use compat calls from v4 support library for all permission checks and requests. Also removed M-only checks and lowered minSDK to 15. Use a SnackBar instead of a Toast to display messages. Change-Id: I53f8e5354e4d99060eeac41241a44bd931afd7a8
Diffstat (limited to 'system')
-rw-r--r--system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java171
-rw-r--r--system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/PermissionUtil.java45
-rw-r--r--system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/RuntimePermissionsFragment.java11
-rw-r--r--system/RuntimePermissions/Application/src/main/java/common/activities/SampleActivityBase.java53
-rw-r--r--system/RuntimePermissions/Application/src/main/res/values/strings.xml3
-rw-r--r--system/RuntimePermissions/template-params.xml35
6 files changed, 210 insertions, 108 deletions
diff --git a/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java b/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java
index 5f38bad8..7abc538c 100644
--- a/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java
+++ b/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java
@@ -16,7 +16,6 @@
package com.example.android.system.runtimepermissions;
-import com.example.android.common.activities.SampleActivityBase;
import com.example.android.common.logger.Log;
import com.example.android.common.logger.LogFragment;
import com.example.android.common.logger.LogWrapper;
@@ -26,15 +25,20 @@ import com.example.android.system.runtimepermissions.contacts.ContactsFragment;
import android.Manifest;
import android.app.Activity;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentTransaction;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.widget.Toast;
import android.widget.ViewAnimator;
+import common.activities.SampleActivityBase;
+
/**
* Launcher Activity that demonstrates the use of runtime permissions for Android M.
* It contains a summary sample description, sample log and a Fragment that calls callbacks on this
@@ -46,15 +50,18 @@ import android.widget.ViewAnimator;
* android.Manifest.permission#WRITE_CONTACTS})) are requested when the 'Show and Add Contacts'
* button is
* clicked to display the first contact in the contacts database and to add a dummy contact
- * directly
- * to it. First, permissions are checked if they have already been granted through {@link
- * android.app.Activity#checkSelfPermission(String)} (wrapped in {@link
- * PermissionUtil#hasSelfPermission(Activity, String)} and {@link PermissionUtil#hasSelfPermission(Activity,
- * String[])} for compatibility). If permissions have not been granted, they are requested through
- * {@link Activity#requestPermissions(String[], int)} and the return value checked in {@link
- * Activity#onRequestPermissionsResult(int, String[], int[])}.
+ * directly to it. Permissions are verified and requested through compat helpers in the support v4
+ * library, in this Activity using {@link ActivityCompat}.
+ * First, permissions are checked if they have already been granted through {@link
+ * ActivityCompat#checkSelfPermission(Context, String)}.
+ * If permissions have not been granted, they are requested through
+ * {@link ActivityCompat#requestPermissions(Activity, String[], int)} and the return value checked
+ * in
+ * a callback to the {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
+ * interface.
* <p>
- * Before requesting permissions, {@link Activity#shouldShowRequestPermissionRationale(String)}
+ * Before requesting permissions, {@link ActivityCompat#shouldShowRequestPermissionRationale(Activity,
+ * String)}
* should be called to provide the user with additional context for the use of permissions if they
* have been denied previously.
* <p>
@@ -73,7 +80,8 @@ import android.widget.ViewAnimator;
* <p>
* (This class is based on the MainActivity used in the SimpleFragment sample template.)
*/
-public class MainActivity extends SampleActivityBase {
+public class MainActivity extends SampleActivityBase
+ implements ActivityCompat.OnRequestPermissionsResultCallback {
public static final String TAG = "MainActivity";
@@ -96,6 +104,10 @@ public class MainActivity extends SampleActivityBase {
// Whether the Log Fragment is currently shown.
private boolean mLogShown;
+ /**
+ * Root of the layout of this Activity.
+ */
+ private View mLayout;
/**
* Called when the 'show camera' button is clicked.
@@ -105,30 +117,57 @@ public class MainActivity extends SampleActivityBase {
Log.i(TAG, "Show camera button pressed. Checking permission.");
// BEGIN_INCLUDE(camera_permission)
// Check if the Camera permission is already available.
- if (PermissionUtil.hasSelfPermission(this, Manifest.permission.CAMERA)) {
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
+ != PackageManager.PERMISSION_GRANTED) {
+ // Camera permission has not been granted.
+
+ requestCameraPermission();
+
+ } else {
+
// Camera permissions is already available, show the camera preview.
Log.i(TAG,
"CAMERA permission has already been granted. Displaying camera preview.");
showCameraPreview();
- } else {
- // Camera permission has not been granted.
- Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission.");
+ }
+ // END_INCLUDE(camera_permission)
+ }
+
+ /**
+ * Requests the Camera permission.
+ * If the permission has been denied previously, a SnackBar will prompt the user to grant the
+ * permission, otherwise it is requested directly.
+ */
+ private void requestCameraPermission() {
+ Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission.");
+
+ // BEGIN_INCLUDE(camera_permission_request)
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.CAMERA)) {
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
- if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
- Log.i(TAG,
- "Displaying camera permission rationale to provide additional context.");
- Toast.makeText(this, R.string.permission_camera_rationale, Toast.LENGTH_SHORT)
- .show();
- }
+ // For example if the user has previously denied the permission.
+ Log.i(TAG,
+ "Displaying camera permission rationale to provide additional context.");
+ Snackbar.make(mLayout, R.string.permission_camera_rationale,
+ Snackbar.LENGTH_INDEFINITE)
+ .setAction(R.string.ok, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ActivityCompat.requestPermissions(MainActivity.this,
+ new String[]{Manifest.permission.CAMERA},
+ REQUEST_CAMERA);
+ }
+ })
+ .show();
+ } else {
- // Request Camera permission
- requestPermissions(new String[]{Manifest.permission.CAMERA},
+ // Camera permission has not been granted yet. Request it directly.
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
- // END_INCLUDE(camera_permission)
-
+ // END_INCLUDE(camera_permission_request)
}
/**
@@ -137,30 +176,63 @@ public class MainActivity extends SampleActivityBase {
*/
public void showContacts(View v) {
Log.i(TAG, "Show contacts button pressed. Checking permissions.");
+
// Verify that all required contact permissions have been granted.
- if (PermissionUtil.hasSelfPermission(this, PERMISSIONS_CONTACT)) {
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED
+ || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ // Contacts permissions have not been granted.
+ Log.i(TAG, "Contact permissions has NOT been granted. Requesting permissions.");
+ requestContactsPermissions();
+
+ } else {
+
+ // Contact permissions have been granted. Show the contacts fragment.
Log.i(TAG,
"Contact permissions have already been granted. Displaying contact details.");
- // Contact permissions have been granted. Show the contacts fragment.
showContactDetails();
- } else {
- // Contacts permissions have not been granted.
- Log.i(TAG, "Contact permissions has NOT been granted. Requesting permission.");
+ }
+ }
+
+ /**
+ * Requests the Contacts permissions.
+ * If the permission has been denied previously, a SnackBar will prompt the user to grant the
+ * permission, otherwise it is requested directly.
+ */
+ private void requestContactsPermissions() {
+ // BEGIN_INCLUDE(contacts_permission_request)
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.READ_CONTACTS)
+ || ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.WRITE_CONTACTS)) {
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
- if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
- Log.i(TAG,
- "Displaying contacts permission rationale to provide additional context.");
- Toast.makeText(this, R.string.permission_contacts_rationale, Toast.LENGTH_SHORT)
- .show();
- }
-
- // contact permissions has not been granted (read and write contacts). Request them.
- requestPermissions(PERMISSIONS_CONTACT, REQUEST_CONTACTS);
+ // For example, if the request has been denied previously.
+ Log.i(TAG,
+ "Displaying contacts permission rationale to provide additional context.");
+
+ // Display a SnackBar with an explanation and a button to trigger the request.
+ Snackbar.make(mLayout, R.string.permission_contacts_rationale,
+ Snackbar.LENGTH_INDEFINITE)
+ .setAction(R.string.ok, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ ActivityCompat
+ .requestPermissions(MainActivity.this, PERMISSIONS_CONTACT,
+ REQUEST_CONTACTS);
+ }
+ })
+ .show();
+ } else {
+ // Contact permissions have not been granted yet. Request them directly.
+ ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT, REQUEST_CONTACTS);
}
+ // END_INCLUDE(contacts_permission_request)
}
+
/**
* Display the {@link CameraPreviewFragment} in the content area if the required Camera
* permission has been granted.
@@ -189,8 +261,8 @@ public class MainActivity extends SampleActivityBase {
* Callback received when a permissions request has been completed.
*/
@Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
- int[] grantResults) {
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA) {
// BEGIN_INCLUDE(permission_result)
@@ -198,14 +270,15 @@ public class MainActivity extends SampleActivityBase {
Log.i(TAG, "Received response for Camera permission request.");
// Check if the only required permission has been granted
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Camera permission has been granted, preview can be displayed
Log.i(TAG, "CAMERA permission has now been granted. Showing preview.");
- Toast.makeText(this, R.string.permision_available_camera, Toast.LENGTH_SHORT)
- .show();
+ Snackbar.make(mLayout, R.string.permision_available_camera,
+ Snackbar.LENGTH_SHORT).show();
} else {
Log.i(TAG, "CAMERA permission was NOT granted.");
- Toast.makeText(this, R.string.permissions_not_granted, Toast.LENGTH_SHORT).show();
+ Snackbar.make(mLayout, R.string.permissions_not_granted,
+ Snackbar.LENGTH_SHORT).show();
}
// END_INCLUDE(permission_result)
@@ -217,11 +290,14 @@ public class MainActivity extends SampleActivityBase {
// checked.
if (PermissionUtil.verifyPermissions(grantResults)) {
// All required permissions have been granted, display contacts fragment.
- Toast.makeText(this, R.string.permision_available_contacts, Toast.LENGTH_SHORT)
+ Snackbar.make(mLayout, R.string.permision_available_contacts,
+ Snackbar.LENGTH_SHORT)
.show();
} else {
Log.i(TAG, "Contacts permissions were NOT granted.");
- Toast.makeText(this, R.string.permissions_not_granted, Toast.LENGTH_SHORT).show();
+ Snackbar.make(mLayout, R.string.permissions_not_granted,
+ Snackbar.LENGTH_SHORT)
+ .show();
}
} else {
@@ -291,6 +367,7 @@ public class MainActivity extends SampleActivityBase {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ mLayout = findViewById(R.id.sample_main_layout);
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
diff --git a/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/PermissionUtil.java b/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/PermissionUtil.java
index d0742ead..b9be6258 100644
--- a/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/PermissionUtil.java
+++ b/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/PermissionUtil.java
@@ -18,7 +18,6 @@ package com.example.android.system.runtimepermissions;
import android.app.Activity;
import android.content.pm.PackageManager;
-import android.os.Build;
/**
* Utility class that wraps access to the runtime permissions API in M and provides basic helper
@@ -33,6 +32,11 @@ public abstract class PermissionUtil {
* @see Activity#onRequestPermissionsResult(int, String[], int[])
*/
public static boolean verifyPermissions(int[] grantResults) {
+ // At least one result must be checked.
+ if(grantResults.length < 1){
+ return false;
+ }
+
// Verify that each required permission has been granted, otherwise return false.
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
@@ -42,43 +46,4 @@ public abstract class PermissionUtil {
return true;
}
- /**
- * Returns true if the Activity has access to all given permissions.
- * Always returns true on platforms below M.
- *
- * @see Activity#checkSelfPermission(String)
- */
- public static boolean hasSelfPermission(Activity activity, String[] permissions) {
- // Below Android M all permissions are granted at install time and are already available.
- if (!isMNC()) {
- return true;
- }
-
- // Verify that all required permissions have been granted
- for (String permission : permissions) {
- if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if the Activity has access to a given permission.
- * Always returns true on platforms below M.
- *
- * @see Activity#checkSelfPermission(String)
- */
- public static boolean hasSelfPermission(Activity activity, String permission) {
- // Below Android M all permissions are granted at install time and are already available.
- if (!isMNC()) {
- return true;
- }
-
- return activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
- }
-
- public static boolean isMNC() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
- }
}
diff --git a/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/RuntimePermissionsFragment.java b/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/RuntimePermissionsFragment.java
index b35bfebc..d38195f5 100644
--- a/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/RuntimePermissionsFragment.java
+++ b/system/RuntimePermissions/Application/src/main/java/com/example/android/system/runtimepermissions/RuntimePermissionsFragment.java
@@ -16,6 +16,7 @@
package com.example.android.system.runtimepermissions;
+import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
@@ -32,16 +33,16 @@ public class RuntimePermissionsFragment extends Fragment {
View root = inflater.inflate(R.layout.fragment_main, null);
// BEGIN_INCLUDE(m_only_permission)
- if (!PermissionUtil.isMNC()) {
+ if (Build.VERSION.SDK_INT < 23) {
/*
- The contacts permissions have been declared in the AndroidManifest for Android M only.
- They are not available on older platforms, so we are hiding the button to access the
- contacts database.
+ The contacts permissions have been declared in the AndroidManifest for Android M and
+ above only. They are not available on older platforms, so we are hiding the button to
+ access the contacts database.
This shows how new runtime-only permissions can be added, that do not apply to older
platform versions. This can be useful for automated updates where additional
permissions might prompt the user on upgrade.
*/
- root.findViewById(R.id.button_camera).setVisibility(View.GONE);
+ root.findViewById(R.id.button_contacts).setVisibility(View.GONE);
}
// END_INCLUDE(m_only_permission)
diff --git a/system/RuntimePermissions/Application/src/main/java/common/activities/SampleActivityBase.java b/system/RuntimePermissions/Application/src/main/java/common/activities/SampleActivityBase.java
new file mode 100644
index 00000000..ac3928ef
--- /dev/null
+++ b/system/RuntimePermissions/Application/src/main/java/common/activities/SampleActivityBase.java
@@ -0,0 +1,53 @@
+/*
+* 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.
+*/
+
+package common.activities;
+
+import com.example.android.common.logger.Log;
+import com.example.android.common.logger.LogWrapper;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+
+/**
+ * Base launcher activity, to handle most of the common plumbing for samples.
+ */
+public class SampleActivityBase extends AppCompatActivity {
+
+ public static final String TAG = "SampleActivityBase";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ initializeLogging();
+ }
+
+ /** Set up targets to receive log data */
+ public void initializeLogging() {
+ // Using Log, front-end to the logging chain, emulates android.util.log method signatures.
+ // Wraps Android's native log framework
+ LogWrapper logWrapper = new LogWrapper();
+ Log.setLogNode(logWrapper);
+
+ Log.i(TAG, "Ready");
+ }
+}
diff --git a/system/RuntimePermissions/Application/src/main/res/values/strings.xml b/system/RuntimePermissions/Application/src/main/res/values/strings.xml
index 82d7b719..edd2c153 100644
--- a/system/RuntimePermissions/Application/src/main/res/values/strings.xml
+++ b/system/RuntimePermissions/Application/src/main/res/values/strings.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <string name="ok">OK</string>
<string name="contacts_string">Total number of contacts: %1$,d\nFirst contact:<b>%2$s</b></string>
<string name="contacts_none">No contacts stored on device.</string>
<string name="contacts_empty">Contacts not loaded.</string>
@@ -17,5 +18,5 @@
<string name="permision_available_contacts">Contacts Permissions have been granted. Contacts screen can now be opened.</string>
<string name="permissions_not_granted">Permissions were not granted.</string>
<string name="permission_camera_rationale">Camera permission is needed to show the camera preview.</string>
- <string name="permission_contacts_rationale">Contacts permissions are needed to demonstrate access to the contacts database.</string>
+ <string name="permission_contacts_rationale">Contacts permissions are needed to demonstrate access.</string>
</resources>
diff --git a/system/RuntimePermissions/template-params.xml b/system/RuntimePermissions/template-params.xml
index 445b6a39..52e9f9fd 100644
--- a/system/RuntimePermissions/template-params.xml
+++ b/system/RuntimePermissions/template-params.xml
@@ -19,23 +19,25 @@
<group>System</group>
<package>com.example.android.system.runtimepermissions</package>
- <minSdk>23</minSdk>
+ <minSdk>15</minSdk>
- <dependency>com.android.support:appcompat-v7:21.+</dependency>
+ <dependency>com.android.support:appcompat-v7:23.0.0</dependency>
+ <dependency>com.android.support:support-v4:23.0.0</dependency>
+ <dependency>com.android.support:design:23.0.0</dependency>
<strings>
<intro>
<![CDATA[
- This sample shows runtime permissions available in the Android M and above.
- Display the log to follow the execution.
- If executed on an Android M device, an additional option to access contacts is shown.
+ This sample shows runtime permissions available in Android M and above.
+ Display the log on screen to follow the execution.
+ If executed on an Android M device, an additional option to access contacts is shown
+ that is declared with optional, M and above only permissions.
]]>
</intro>
</strings>
<template src="base"/>
<common src="logger"/>
- <common src="activities"/>
<metadata>
<status>DRAFT</status>
@@ -51,14 +53,15 @@
</screenshots>
<api_refs>
<android>android.app.Activity</android>
+ <android>android.support.v4.app.ActivityCompat</android>
<android>android.Manifest.permission</android>
</api_refs>
<description>
<![CDATA[
-This sample shows runtime permissions available in the Android M and above.
-It shows how to check and request permissions at runtime and how to declare permissions for M-devices
-only.
+This sample shows runtime permissions available in Android M and above.
+It shows how to check and request permissions at runtime, handle backwards compatibility using the
+support library and how to declare optional permissions for M-devices only.
]]>
</description>
@@ -68,17 +71,19 @@ Android M introduced runtime permissions. Applications targeting M and above nee
permissions at runtime.
All permissions still need to be declared in the AndroidManifest. However, when accessing APIs that
require a permission, the Activity or Fragment has to verify that the permission has been granted
-or request the missing permissions. Permissions are checked through
-Activity#requestPermissions(String[], int) and Fragment#requestPermissions(String[], int).
-Permission requests are sent through Activity#requestPermissions(String[]), and the response
-received in the callback Activity#onRequestPermissionsResult(int, permissions[], int[]).
+or request the missing permissions using calls through the support library. Permissions are checked
+through ActivityCompat#checkSelfPermission(Context, String) or
+ContextCompat#checkSelfPermission(Context, String).
+Permission are requested through ActivityCompat#requestPermissions(Activity, String[], int), and the response
+received in a callback to ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[]).
Applications can provide an additional rational for the use of permissions after calling
-Activity#shouldShowRequestPermissionRationale(String). This call will return true if the
+ActivityCompat#shouldShowRequestPermissionRationale(Activity,String). This call will return true if the
application should provide the user with more context on why the requested permissions is needed,
for example if the permission request has been denied before.
If an application targets an SDK below M, all permissions are granted at runtime and are available
-when the application is running. However, if permissions have been turned off in the system settings
+when the application is running. The support library calls handle these checks appropriately.
+However, if permissions have been turned off in the system settings
for an application targeting an SDK below M, the API will return empty or no data.
]]>
</intro>