summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrevor Johns <trevorjohns@google.com>2015-08-20 16:23:13 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-08-20 16:23:13 +0000
commit55dcdfb2982e50d7d127aec80edd4dc910e6a960 (patch)
tree852019b580d1e6b187148dc34302808ac8e2a838
parent7eca3d4cf54cd4bb670d44dcd6af80306ca25d27 (diff)
parentc8e94bf583adc38c36e968eb00f21a8b8ca70257 (diff)
downloadandroid_development-55dcdfb2982e50d7d127aec80edd4dc910e6a960.tar.gz
android_development-55dcdfb2982e50d7d127aec80edd4dc910e6a960.tar.bz2
android_development-55dcdfb2982e50d7d127aec80edd4dc910e6a960.zip
am c8e94bf5: Sync mnc-dev sample prebuilts
* commit 'c8e94bf583adc38c36e968eb00f21a8b8ca70257': Sync mnc-dev sample prebuilts
-rw-r--r--samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.pngbin4199 -> 2323 bytes
-rw-r--r--samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.pngbin2535 -> 1551 bytes
-rw-r--r--samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.pngbin6022 -> 3202 bytes
-rw-r--r--samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.pngbin11040 -> 5061 bytes
-rw-r--r--samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java96
-rw-r--r--samples/browseable/BluetoothAdvertisements/AndroidManifest.xml29
-rw-r--r--samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.pngbin4199 -> 2259 bytes
-rw-r--r--samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.pngbin2535 -> 1540 bytes
-rw-r--r--samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.pngbin6022 -> 3056 bytes
-rw-r--r--samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.pngbin11040 -> 4886 bytes
-rw-r--r--samples/browseable/BluetoothAdvertisements/res/values/strings.xml3
-rw-r--r--samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java223
-rw-r--r--samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java223
-rw-r--r--samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java4
-rw-r--r--samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java6
-rw-r--r--samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java3
-rw-r--r--samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java15
-rw-r--r--samples/browseable/RuntimePermissions/_index.jd7
-rw-r--r--samples/browseable/RuntimePermissions/res/values/base-strings.xml7
-rw-r--r--samples/browseable/RuntimePermissions/res/values/strings.xml3
-rw-r--r--samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java171
-rw-r--r--samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java45
-rw-r--r--samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java11
-rw-r--r--samples/browseable/RuntimePermissions/src/common.activities/SampleActivityBase.java53
-rw-r--r--samples/browseable/RuntimePermissionsBasic/_index.jd1
-rw-r--r--samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml37
-rw-r--r--samples/browseable/RuntimePermissionsBasic/res/values/base-strings.xml1
-rw-r--r--samples/browseable/RuntimePermissionsBasic/src/com.example.android.basicpermissions/MainActivity.java109
28 files changed, 729 insertions, 318 deletions
diff --git a/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png
index b1efaf4b2..4252db138 100644
--- a/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png
+++ b/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png
index f5f9244f2..969456ee0 100644
--- a/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png
+++ b/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f06..608151afd 100644
--- a/samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.png
+++ b/samples/browseable/BasicGestureDetect/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1f4..23ffa9a08 100644
--- a/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png
+++ b/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java
index 2e2921d46..19b588a66 100644
--- a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java
+++ b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java
@@ -16,6 +16,8 @@
package com.example.android.basicgesturedetect;
+import android.annotation.TargetApi;
+import android.os.Build;
import android.view.GestureDetector;
import android.view.MotionEvent;
@@ -29,7 +31,7 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
// Up motion completing a single tap occurred.
- Log.i(TAG, "Single Tap Up");
+ Log.i(TAG, "Single Tap Up" + getTouchType(e));
return false;
}
@@ -37,14 +39,14 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener {
public void onLongPress(MotionEvent e) {
// Touch has been long enough to indicate a long press.
// Does not indicate motion is complete yet (no up event necessarily)
- Log.i(TAG, "Long Press");
+ Log.i(TAG, "Long Press" + getTouchType(e));
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
// User attempted to scroll
- Log.i(TAG, "Scroll");
+ Log.i(TAG, "Scroll" + getTouchType(e1));
return false;
}
@@ -52,27 +54,27 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener {
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// Fling event occurred. Notification of this one happens after an "up" event.
- Log.i(TAG, "Fling");
+ Log.i(TAG, "Fling" + getTouchType(e1));
return false;
}
@Override
public void onShowPress(MotionEvent e) {
// User performed a down event, and hasn't moved yet.
- Log.i(TAG, "Show Press");
+ Log.i(TAG, "Show Press" + getTouchType(e));
}
@Override
public boolean onDown(MotionEvent e) {
// "Down" event - User touched the screen.
- Log.i(TAG, "Down");
+ Log.i(TAG, "Down" + getTouchType(e));
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
// User tapped the screen twice.
- Log.i(TAG, "Double tap");
+ Log.i(TAG, "Double tap" + getTouchType(e));
return false;
}
@@ -81,7 +83,7 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener {
// Since double-tap is actually several events which are considered one aggregate
// gesture, there's a separate callback for an individual event within the doubletap
// occurring. This occurs for down, up, and move.
- Log.i(TAG, "Event within double tap");
+ Log.i(TAG, "Event within double tap" + getTouchType(e));
return false;
}
@@ -89,8 +91,84 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener {
public boolean onSingleTapConfirmed(MotionEvent e) {
// A confirmed single-tap event has occurred. Only called when the detector has
// determined that the first tap stands alone, and is not part of a double tap.
- Log.i(TAG, "Single tap confirmed");
+ Log.i(TAG, "Single tap confirmed" + getTouchType(e));
return false;
}
// END_INCLUDE(init_gestureListener)
+
+
+ /**
+ * Returns a human-readable string describing the type of touch that triggered a MotionEvent.
+ */
+
+ private static String getTouchType(MotionEvent e){
+
+ String touchTypeDescription = " ";
+ int touchType = e.getToolType(0);
+
+ switch (touchType) {
+ case MotionEvent.TOOL_TYPE_FINGER:
+ touchTypeDescription += "(finger)";
+ break;
+ case MotionEvent.TOOL_TYPE_STYLUS:
+ touchTypeDescription += "(stylus, ";
+ //Get some additional information about the stylus touch
+ float stylusPressure = e.getPressure();
+ touchTypeDescription += "pressure: " + stylusPressure;
+
+ if(Build.VERSION.SDK_INT >= 21) {
+ touchTypeDescription += ", buttons pressed: " + getButtonsPressed(e);
+ }
+
+ touchTypeDescription += ")";
+ break;
+ case MotionEvent.TOOL_TYPE_ERASER:
+ touchTypeDescription += "(eraser)";
+ break;
+ case MotionEvent.TOOL_TYPE_MOUSE:
+ touchTypeDescription += "(mouse)";
+ break;
+ default:
+ touchTypeDescription += "(unknown tool)";
+ break;
+ }
+
+ return touchTypeDescription;
+ }
+
+ /**
+ * Returns a human-readable string listing all the stylus buttons that were pressed when the
+ * input MotionEvent occurred.
+ */
+ @TargetApi(21)
+ private static String getButtonsPressed(MotionEvent e){
+ String buttons = "";
+
+ if(e.isButtonPressed(MotionEvent.BUTTON_PRIMARY)){
+ buttons += " primary";
+ }
+
+ if(e.isButtonPressed(MotionEvent.BUTTON_SECONDARY)){
+ buttons += " secondary";
+ }
+
+ if(e.isButtonPressed(MotionEvent.BUTTON_TERTIARY)){
+ buttons += " tertiary";
+ }
+
+ if(e.isButtonPressed(MotionEvent.BUTTON_BACK)){
+ buttons += " back";
+ }
+
+ if(e.isButtonPressed(MotionEvent.BUTTON_FORWARD)){
+ buttons += " forward";
+ }
+
+ if (buttons.equals("")){
+ buttons = "none";
+ }
+
+ return buttons;
+ }
+
}
diff --git a/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml b/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml
index 48084fc97..cd2a65e59 100644
--- a/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml
+++ b/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2013 The Android Open Source Project
@@ -17,23 +17,32 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.bluetoothadvertisements"
android:versionCode="1"
- android:versionName="1.0">
+ android:versionName="1.0" >
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
- <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
- <application android:allowBackup="true"
- android:label="@string/app_name"
+ <application
+ android:allowBackup="true"
android:icon="@drawable/ic_launcher"
- android:theme="@style/AppTheme">
-
- <activity android:name=".MainActivity"
- android:label="@string/app_name">
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <!-- Service to handle BLE Advertising - Using a service allows advertising to continue
+ when the app is no longer on screen in a reliable manner. -->
+ <service
+ android:name=".AdvertiserService"
+ android:enabled="true"
+ android:exported="false" >
+ </service>
</application>
</manifest>
diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png
index b1efaf4b2..48d646544 100644
--- a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png
+++ b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png
index f5f9244f2..42b3b1edf 100644
--- a/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png
+++ b/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png
index 5d07b3f06..13663e8be 100644
--- a/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png
+++ b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png
index 6ef21e1f4..5e84f3a58 100644
--- a/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png
+++ b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/browseable/BluetoothAdvertisements/res/values/strings.xml b/samples/browseable/BluetoothAdvertisements/res/values/strings.xml
index 197178d00..927f3b603 100644
--- a/samples/browseable/BluetoothAdvertisements/res/values/strings.xml
+++ b/samples/browseable/BluetoothAdvertisements/res/values/strings.xml
@@ -26,5 +26,8 @@
<string name="seconds">seconds.</string>
<string name="scan_start_toast">Scanning for</string>
<string name="already_scanning">Scanning already started.</string>
+ <string name="no_name">(no name)</string>
+ <string name="start_error_unknown">unknown error</string>
+ <string name="advertising_timedout">Advertising stopped due to timeout.</string>
</resources> \ No newline at end of file
diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java
index f3645fc29..c97b90435 100644
--- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java
+++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java
@@ -16,11 +16,11 @@
package com.example.android.bluetoothadvertisements;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.AdvertiseCallback;
-import android.bluetooth.le.AdvertiseData;
-import android.bluetooth.le.AdvertiseSettings;
-import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@@ -32,175 +32,144 @@ import android.widget.Toast;
/**
* Allows user to start & stop Bluetooth LE Advertising of their device.
*/
-public class AdvertiserFragment extends Fragment {
-
- private BluetoothAdapter mBluetoothAdapter;
-
- private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
-
- private AdvertiseCallback mAdvertiseCallback;
+public class AdvertiserFragment extends Fragment implements View.OnClickListener {
+ /**
+ * Lets user toggle BLE Advertising.
+ */
private Switch mSwitch;
/**
- * Must be called after object creation by MainActivity.
- *
- * @param btAdapter the local BluetoothAdapter
+ * Listens for notifications that the {@code AdvertiserService} has failed to start advertising.
+ * This Receiver deals with Fragment UI elements and only needs to be active when the Fragment
+ * is on-screen, so it's defined and registered in code instead of the Manifest.
*/
- public void setBluetoothAdapter(BluetoothAdapter btAdapter) {
- this.mBluetoothAdapter = btAdapter;
- mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
- }
+ private BroadcastReceiver advertisingFailureReceiver;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setRetainInstance(true);
+
+ advertisingFailureReceiver = new BroadcastReceiver() {
+
+ /**
+ * Receives Advertising error codes from {@code AdvertiserService} and displays error messages
+ * to the user. Sets the advertising toggle to 'false.'
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ int errorCode = intent.getIntExtra(AdvertiserService.ADVERTISING_FAILED_EXTRA_CODE, -1);
+
+ mSwitch.setChecked(false);
+
+ String errorMessage = getString(R.string.start_error_prefix);
+ switch (errorCode) {
+ case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
+ errorMessage += " " + getString(R.string.start_error_already_started);
+ break;
+ case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
+ errorMessage += " " + getString(R.string.start_error_too_large);
+ break;
+ case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
+ errorMessage += " " + getString(R.string.start_error_unsupported);
+ break;
+ case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
+ errorMessage += " " + getString(R.string.start_error_internal);
+ break;
+ case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
+ errorMessage += " " + getString(R.string.start_error_too_many);
+ break;
+ case AdvertiserService.ADVERTISING_TIMED_OUT:
+ errorMessage = " " + getString(R.string.advertising_timedout);
+ break;
+ default:
+ errorMessage += " " + getString(R.string.start_error_unknown);
+ }
+
+ Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show();
+ }
+ };
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_advertiser, container, false);
mSwitch = (Switch) view.findViewById(R.id.advertise_switch);
- mSwitch.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- onSwitchClicked(v);
- }
- });
+ mSwitch.setOnClickListener(this);
return view;
}
- @Override
- public void onStop() {
- super.onStop();
-
- if(mAdvertiseCallback != null){
- stopAdvertising();
- }
- }
-
/**
- * Called when switch is toggled - starts or stops advertising.
- *
- * @param view is the Switch View object
+ * When app comes on screen, check if BLE Advertisements are running, set switch accordingly,
+ * and register the Receiver to be notified if Advertising fails.
*/
- public void onSwitchClicked(View view) {
-
- // Is the toggle on?
- boolean on = ((Switch) view).isChecked();
+ @Override
+ public void onResume() {
+ super.onResume();
- if (on) {
- startAdvertising();
+ if (AdvertiserService.running) {
+ mSwitch.setChecked(true);
} else {
- stopAdvertising();
+ mSwitch.setChecked(false);
}
+
+ IntentFilter failureFilter = new IntentFilter(AdvertiserService.ADVERTISING_FAILED);
+ getActivity().registerReceiver(advertisingFailureReceiver, failureFilter);
+
}
/**
- * Starts BLE Advertising.
+ * When app goes off screen, unregister the Advertising failure Receiver to stop memory leaks.
+ * (and because the app doesn't care if Advertising fails while the UI isn't active)
*/
- private void startAdvertising() {
-
- mAdvertiseCallback = new SampleAdvertiseCallback();
-
- if (mBluetoothLeAdvertiser != null) {
- mBluetoothLeAdvertiser.startAdvertising(buildAdvertiseSettings(), buildAdvertiseData(),
- mAdvertiseCallback);
- } else {
- mSwitch.setChecked(false);
- Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show();
- }
+ @Override
+ public void onPause() {
+ super.onPause();
+ getActivity().unregisterReceiver(advertisingFailureReceiver);
}
/**
- * Stops BLE Advertising.
+ * Returns Intent addressed to the {@code AdvertiserService} class.
*/
- private void stopAdvertising() {
-
- if (mBluetoothLeAdvertiser != null) {
-
- mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
- mAdvertiseCallback = null;
-
- } else {
- mSwitch.setChecked(false);
- Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show();
- }
+ private static Intent getServiceIntent(Context c) {
+ return new Intent(c, AdvertiserService.class);
}
/**
- * Returns an AdvertiseData object which includes the Service UUID and Device Name.
+ * Called when switch is toggled - starts or stops advertising.
*/
- private AdvertiseData buildAdvertiseData() {
-
- // Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements.
- // This includes everything put into AdvertiseData including UUIDs, device info, &
- // arbitrary service or manufacturer data.
- // Attempting to send packets over this limit will result in a failure with error code
- // AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the
- // onStartFailure() method of an AdvertiseCallback implementation.
-
- AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
- dataBuilder.addServiceUuid(Constants.Service_UUID);
- dataBuilder.setIncludeDeviceName(true);
+ @Override
+ public void onClick(View v) {
+ // Is the toggle on?
+ boolean on = ((Switch) v).isChecked();
- return dataBuilder.build();
+ if (on) {
+ startAdvertising();
+ } else {
+ stopAdvertising();
+ }
}
/**
- * Returns an AdvertiseSettings object set to use low power (to help preserve battery life).
+ * Starts BLE Advertising by starting {@code AdvertiserService}.
*/
- private AdvertiseSettings buildAdvertiseSettings() {
- AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
- settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
-
- return settingsBuilder.build();
+ private void startAdvertising() {
+ Context c = getActivity();
+ c.startService(getServiceIntent(c));
}
/**
- * Custom callback after Advertising succeeds or fails to start.
+ * Stops BLE Advertising by stopping {@code AdvertiserService}.
*/
- private class SampleAdvertiseCallback extends AdvertiseCallback {
-
- @Override
- public void onStartFailure(int errorCode) {
- super.onStartFailure(errorCode);
-
- mSwitch.setChecked(false);
-
- String errorMessage = getString(R.string.start_error_prefix);
- switch (errorCode) {
- case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
- errorMessage += " " + getString(R.string.start_error_already_started);
- break;
- case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
- errorMessage += " " + getString(R.string.start_error_too_large);
- break;
- case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
- errorMessage += " " + getString(R.string.start_error_unsupported);
- break;
- case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
- errorMessage += " " + getString(R.string.start_error_internal);
- break;
- case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
- errorMessage += " " + getString(R.string.start_error_too_many);
- break;
- }
-
- Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show();
-
- }
-
- @Override
- public void onStartSuccess(AdvertiseSettings settingsInEffect) {
- super.onStartSuccess(settingsInEffect);
- // Don't need to do anything here, advertising successfully started.
- }
+ private void stopAdvertising() {
+ Context c = getActivity();
+ c.stopService(getServiceIntent(c));
+ mSwitch.setChecked(false);
}
-}
+} \ No newline at end of file
diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java
new file mode 100644
index 000000000..0cc3ff03e
--- /dev/null
+++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java
@@ -0,0 +1,223 @@
+package com.example.android.bluetoothadvertisements;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Manages BLE Advertising independent of the main app.
+ * If the app goes off screen (or gets killed completely) advertising can continue because this
+ * Service is maintaining the necessary Callback in memory.
+ */
+public class AdvertiserService extends Service {
+
+ private static final String TAG = AdvertiserService.class.getSimpleName();
+
+ /**
+ * A global variable to let AdvertiserFragment check if the Service is running without needing
+ * to start or bind to it.
+ * This is the best practice method as defined here:
+ * https://groups.google.com/forum/#!topic/android-developers/jEvXMWgbgzE
+ */
+ public static boolean running = false;
+
+ public static final String ADVERTISING_FAILED =
+ "com.example.android.bluetoothadvertisements.advertising_failed";
+
+ public static final String ADVERTISING_FAILED_EXTRA_CODE = "failureCode";
+
+ public static final int ADVERTISING_TIMED_OUT = 6;
+
+ private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
+
+ private AdvertiseCallback mAdvertiseCallback;
+
+ private Handler mHandler;
+
+ private Runnable timeoutRunnable;
+
+ /**
+ * Length of time to allow advertising before automatically shutting off. (10 minutes)
+ */
+ private long TIMEOUT = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES);
+
+ @Override
+ public void onCreate() {
+ running = true;
+ initialize();
+ startAdvertising();
+ setTimeout();
+ super.onCreate();
+ }
+
+ @Override
+ public void onDestroy() {
+ /**
+ * Note that onDestroy is not guaranteed to be called quickly or at all. Services exist at
+ * the whim of the system, and onDestroy can be delayed or skipped entirely if memory need
+ * is critical.
+ */
+ running = false;
+ stopAdvertising();
+ mHandler.removeCallbacks(timeoutRunnable);
+ super.onDestroy();
+ }
+
+ /**
+ * Required for extending service, but this will be a Started Service only, so no need for
+ * binding.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * Get references to system Bluetooth objects if we don't have them already.
+ */
+ private void initialize() {
+ if (mBluetoothLeAdvertiser == null) {
+ BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+ if (mBluetoothManager != null) {
+ BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
+ if (mBluetoothAdapter != null) {
+ mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
+ } else {
+ Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show();
+ }
+ } else {
+ Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show();
+ }
+ }
+
+ }
+
+ /**
+ * Starts a delayed Runnable that will cause the BLE Advertising to timeout and stop after a
+ * set amount of time.
+ */
+ private void setTimeout(){
+ mHandler = new Handler();
+ timeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "AdvertiserService has reached timeout of "+TIMEOUT+" milliseconds, stopping advertising.");
+ sendFailureIntent(ADVERTISING_TIMED_OUT);
+ stopSelf();
+ }
+ };
+ mHandler.postDelayed(timeoutRunnable, TIMEOUT);
+ }
+
+ /**
+ * Starts BLE Advertising.
+ */
+ private void startAdvertising() {
+ Log.d(TAG, "Service: Starting Advertising");
+
+ if (mAdvertiseCallback == null) {
+ AdvertiseSettings settings = buildAdvertiseSettings();
+ AdvertiseData data = buildAdvertiseData();
+ mAdvertiseCallback = new SampleAdvertiseCallback();
+
+ if (mBluetoothLeAdvertiser != null) {
+ mBluetoothLeAdvertiser.startAdvertising(settings, data,
+ mAdvertiseCallback);
+ }
+ }
+ }
+
+ /**
+ * Stops BLE Advertising.
+ */
+ private void stopAdvertising() {
+ Log.d(TAG, "Service: Stopping Advertising");
+ if (mBluetoothLeAdvertiser != null) {
+ mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
+ mAdvertiseCallback = null;
+ }
+ }
+
+ /**
+ * Returns an AdvertiseData object which includes the Service UUID and Device Name.
+ */
+ private AdvertiseData buildAdvertiseData() {
+
+ /**
+ * Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements.
+ * This includes everything put into AdvertiseData including UUIDs, device info, &
+ * arbitrary service or manufacturer data.
+ * Attempting to send packets over this limit will result in a failure with error code
+ * AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the
+ * onStartFailure() method of an AdvertiseCallback implementation.
+ */
+
+ AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
+ dataBuilder.addServiceUuid(Constants.Service_UUID);
+ dataBuilder.setIncludeDeviceName(true);
+
+ /* For example - this will cause advertising to fail (exceeds size limit) */
+ //String failureData = "asdghkajsghalkxcjhfa;sghtalksjcfhalskfjhasldkjfhdskf";
+ //dataBuilder.addServiceData(Constants.Service_UUID, failureData.getBytes());
+
+ return dataBuilder.build();
+ }
+
+ /**
+ * Returns an AdvertiseSettings object set to use low power (to help preserve battery life)
+ * and disable the built-in timeout since this code uses its own timeout runnable.
+ */
+ private AdvertiseSettings buildAdvertiseSettings() {
+ AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
+ settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
+ settingsBuilder.setTimeout(0);
+ return settingsBuilder.build();
+ }
+
+ /**
+ * Custom callback after Advertising succeeds or fails to start. Broadcasts the error code
+ * in an Intent to be picked up by AdvertiserFragment and stops this Service.
+ */
+ private class SampleAdvertiseCallback extends AdvertiseCallback {
+
+ @Override
+ public void onStartFailure(int errorCode) {
+ super.onStartFailure(errorCode);
+
+ Log.d(TAG, "Advertising failed");
+ sendFailureIntent(errorCode);
+ stopSelf();
+
+ }
+
+ @Override
+ public void onStartSuccess(AdvertiseSettings settingsInEffect) {
+ super.onStartSuccess(settingsInEffect);
+ Log.d(TAG, "Advertising successfully started");
+ }
+ }
+
+ /**
+ * Builds and sends a broadcast intent indicating Advertising has failed. Includes the error
+ * code as an extra. This is intended to be picked up by the {@code AdvertiserFragment}.
+ */
+ private void sendFailureIntent(int errorCode){
+ Intent failureIntent = new Intent();
+ failureIntent.setAction(ADVERTISING_FAILED);
+ failureIntent.putExtra(ADVERTISING_FAILED_EXTRA_CODE, errorCode);
+ sendBroadcast(failureIntent);
+ }
+
+} \ No newline at end of file
diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java
index 871935d9d..7ea389160 100644
--- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java
+++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java
@@ -39,7 +39,7 @@ public class MainActivity extends FragmentActivity {
setContentView(R.layout.activity_main);
setTitle(R.string.activity_main_title);
- if (savedInstanceState == null ) {
+ if (savedInstanceState == null) {
mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE))
.getAdapter();
@@ -112,11 +112,11 @@ public class MainActivity extends FragmentActivity {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
ScannerFragment scannerFragment = new ScannerFragment();
+ // Fragments can't access system services directly, so pass it the BluetoothAdapter
scannerFragment.setBluetoothAdapter(mBluetoothAdapter);
transaction.replace(R.id.scanner_fragment_container, scannerFragment);
AdvertiserFragment advertiserFragment = new AdvertiserFragment();
- advertiserFragment.setBluetoothAdapter(mBluetoothAdapter);
transaction.replace(R.id.advertiser_fragment_container, advertiserFragment);
transaction.commit();
diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java
index f3c141d36..5a9b95483 100644
--- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java
+++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java
@@ -75,7 +75,11 @@ public class ScanResultAdapter extends BaseAdapter {
ScanResult scanResult = mArrayList.get(position);
- deviceNameView.setText(scanResult.getDevice().getName());
+ String name = scanResult.getDevice().getName();
+ if (name == null) {
+ name = mContext.getResources().getString(R.string.no_name);
+ }
+ deviceNameView.setText(name);
deviceAddressView.setText(scanResult.getDevice().getAddress());
lastSeenView.setText(getTimeSinceString(mContext, scanResult.getTimestampNanos()));
diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java
index ebb1ad085..4f5c2aa59 100644
--- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java
+++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java
@@ -83,7 +83,7 @@ public class ScannerFragment extends ListFragment {
// We could get a LayoutInflater from the ApplicationContext but it messes with the
// default theme, so generate it from getActivity() and pass it in separately.
mAdapter = new ScanResultAdapter(getActivity().getApplicationContext(),
- LayoutInflater.from(getActivity()));
+ LayoutInflater.from(getActivity()));
mHandler = new Handler();
}
@@ -180,6 +180,7 @@ public class ScannerFragment extends ListFragment {
List<ScanFilter> scanFilters = new ArrayList<>();
ScanFilter.Builder builder = new ScanFilter.Builder();
+ // Comment out the below line to see all BLE devices around you
builder.setServiceUuid(Constants.Service_UUID);
scanFilters.add(builder.build());
diff --git a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java
index 8909f752f..b17ebb0ba 100644
--- a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java
+++ b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java
@@ -16,6 +16,7 @@
package com.example.android.fingerprintdialog;
+import android.app.Activity;
import android.app.DialogFragment;
import android.content.SharedPreferences;
import android.hardware.fingerprint.FingerprintManager;
@@ -54,6 +55,7 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
private FingerprintManager.CryptoObject mCryptoObject;
private FingerprintUiHelper mFingerprintUiHelper;
+ private MainActivity mActivity;
@Inject FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder;
@Inject InputMethodManager mInputMethodManager;
@@ -135,6 +137,12 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
mFingerprintUiHelper.stopListening();
}
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mActivity = (MainActivity) activity;
+ }
+
/**
* Sets the crypto object to be passed in when authenticating with fingerprint.
*/
@@ -167,7 +175,6 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
if (!checkPassword(mPassword.getText().toString())) {
return;
}
- MainActivity activity = ((MainActivity) getActivity());
if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
@@ -176,12 +183,12 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
if (mUseFingerprintFutureCheckBox.isChecked()) {
// Re-create the key so that fingerprints including new ones are validated.
- activity.createKey();
+ mActivity.createKey();
mStage = Stage.FINGERPRINT;
}
}
mPassword.setText("");
- ((MainActivity) getActivity()).onPurchased(false /* without Fingerprint */);
+ mActivity.onPurchased(false /* without Fingerprint */);
dismiss();
}
@@ -238,7 +245,7 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
public void onAuthenticated() {
// Callback from FingerprintUiHelper. Let the activity know that authentication was
// successful.
- ((MainActivity) getActivity()).onPurchased(true /* withFingerprint */);
+ mActivity.onPurchased(true /* withFingerprint */);
dismiss();
}
diff --git a/samples/browseable/RuntimePermissions/_index.jd b/samples/browseable/RuntimePermissions/_index.jd
index 75c6fee3e..a78f3b0cd 100644
--- a/samples/browseable/RuntimePermissions/_index.jd
+++ b/samples/browseable/RuntimePermissions/_index.jd
@@ -5,8 +5,9 @@ sample.group=System
<p>
- 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.
</p>
diff --git a/samples/browseable/RuntimePermissions/res/values/base-strings.xml b/samples/browseable/RuntimePermissions/res/values/base-strings.xml
index 58d75f931..33c535ac6 100644
--- a/samples/browseable/RuntimePermissions/res/values/base-strings.xml
+++ b/samples/browseable/RuntimePermissions/res/values/base-strings.xml
@@ -21,9 +21,10 @@
<![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.
]]>
diff --git a/samples/browseable/RuntimePermissions/res/values/strings.xml b/samples/browseable/RuntimePermissions/res/values/strings.xml
index 82d7b719d..edd2c1532 100644
--- a/samples/browseable/RuntimePermissions/res/values/strings.xml
+++ b/samples/browseable/RuntimePermissions/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/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java
index 5f38bad8d..7abc538cf 100644
--- a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java
+++ b/samples/browseable/RuntimePermissions/src/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/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java
index d0742ead9..b9be6258e 100644
--- a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java
+++ b/samples/browseable/RuntimePermissions/src/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/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java
index b35bfebc0..d38195f57 100644
--- a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java
+++ b/samples/browseable/RuntimePermissions/src/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/samples/browseable/RuntimePermissions/src/common.activities/SampleActivityBase.java b/samples/browseable/RuntimePermissions/src/common.activities/SampleActivityBase.java
new file mode 100644
index 000000000..ac3928ef0
--- /dev/null
+++ b/samples/browseable/RuntimePermissions/src/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/samples/browseable/RuntimePermissionsBasic/_index.jd b/samples/browseable/RuntimePermissionsBasic/_index.jd
index c4b5d4ca8..4fe611862 100644
--- a/samples/browseable/RuntimePermissionsBasic/_index.jd
+++ b/samples/browseable/RuntimePermissionsBasic/_index.jd
@@ -5,7 +5,6 @@ sample.group=System
<p>
- This sample shows runtime permissions available in the Android M and above.
This sample shows a basic implementation for requesting permissions at runtime. Click the button to request the Camera permission and open a full-screen camera preview.
Note: The "RuntimePermissions" sample provides a more complete overview over the runtime permission features available.
diff --git a/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml b/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml
index c3bf99c5f..146b8b1e7 100644
--- a/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml
+++ b/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml
@@ -14,27 +14,28 @@
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:paddingLeft="@dimen/horizontal_page_margin"
- android:paddingRight="@dimen/horizontal_page_margin"
- android:paddingTop="@dimen/vertical_page_margin"
- android:paddingBottom="@dimen/vertical_page_margin"
- android:orientation="vertical"
- tools:context=".MainActivity">
+<LinearLayout android:id="@+id/main_layout"
+ 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"
+ android:paddingBottom="@dimen/vertical_page_margin"
+ android:paddingLeft="@dimen/horizontal_page_margin"
+ android:paddingRight="@dimen/horizontal_page_margin"
+ android:paddingTop="@dimen/vertical_page_margin"
+ tools:context=".MainActivity">
<TextView
- android:text="@string/intro"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/horizontal_page_margin"/>
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/horizontal_page_margin"
+ android:text="@string/intro" />
<Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Open Camera Preview"
- android:id="@+id/button_open_camera"/>
+ android:id="@+id/button_open_camera"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Open Camera Preview" />
</LinearLayout>
diff --git a/samples/browseable/RuntimePermissionsBasic/res/values/base-strings.xml b/samples/browseable/RuntimePermissionsBasic/res/values/base-strings.xml
index 1a6b6befa..46f6ed837 100644
--- a/samples/browseable/RuntimePermissionsBasic/res/values/base-strings.xml
+++ b/samples/browseable/RuntimePermissionsBasic/res/values/base-strings.xml
@@ -21,7 +21,6 @@
<![CDATA[
- This sample shows runtime permissions available in the Android M and above.
This sample shows a basic implementation for requesting permissions at runtime. Click the button to request the Camera permission and open a full-screen camera preview.
Note: The "RuntimePermissions" sample provides a more complete overview over the runtime permission features available.
diff --git a/samples/browseable/RuntimePermissionsBasic/src/com.example.android.basicpermissions/MainActivity.java b/samples/browseable/RuntimePermissionsBasic/src/com.example.android.basicpermissions/MainActivity.java
index 9fa646d7e..f534d4077 100644
--- a/samples/browseable/RuntimePermissionsBasic/src/com.example.android.basicpermissions/MainActivity.java
+++ b/samples/browseable/RuntimePermissionsBasic/src/com.example.android.basicpermissions/MainActivity.java
@@ -20,13 +20,15 @@ import com.example.android.basicpermissions.camera.CameraPreviewActivity;
import android.Manifest;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.os.Build;
import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
-import android.widget.Toast;
/**
* Launcher Activity that demonstrates the use of runtime permissions for Android M.
@@ -36,22 +38,32 @@ import android.widget.Toast;
* the permission has been granted.
* <p>
* First, the status of the Camera permission is checked using {@link
- * Activity#checkSelfPermission(String)}.
+ * ActivityCompat#checkSelfPermission(Context, String)}
* If it has not been granted ({@link PackageManager#PERMISSION_GRANTED}), it is requested by
* calling
- * {@link Activity#requestPermissions(String[], int)}. The result of the request is returned in
- * {@link Activity#onRequestPermissionsResult(int, String[], int[])}, which starts {@link
- * CameraPreviewActivity}
- * if the permission has been granted.
+ * {@link ActivityCompat#requestPermissions(Activity, String[], int)}. The result of the request is
+ * returned to the
+ * {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}, which starts
+ * {@link
+ * CameraPreviewActivity} if the permission has been granted.
+ * <p>
+ * Note that there is no need to check the API level, the support library
+ * already takes care of this. Similar helper methods for permissions are also available in
+ * ({@link ActivityCompat},
+ * {@link android.support.v4.content.ContextCompat} and {@link android.support.v4.app.Fragment}).
*/
-public class MainActivity extends Activity {
+public class MainActivity extends AppCompatActivity
+ implements ActivityCompat.OnRequestPermissionsResultCallback {
private static final int PERMISSION_REQUEST_CAMERA = 0;
+ private View mLayout;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ mLayout = findViewById(R.id.main_layout);
// Register a listener for the 'Show Camera Preview' button.
Button b = (Button) findViewById(R.id.button_open_camera);
@@ -69,15 +81,16 @@ public class MainActivity extends Activity {
// BEGIN_INCLUDE(onRequestPermissionsResult)
if (requestCode == PERMISSION_REQUEST_CAMERA) {
// Request for camera permission.
- if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission has been granted. Start camera preview Activity.
- Toast.makeText(this, "Camera permission was granted. Starting preview.",
- Toast.LENGTH_SHORT)
+ Snackbar.make(mLayout, "Camera permission was granted. Starting preview.",
+ Snackbar.LENGTH_SHORT)
.show();
startCamera();
} else {
// Permission request was denied.
- Toast.makeText(this, "Camera permission request was denied.", Toast.LENGTH_SHORT)
+ Snackbar.make(mLayout, "Camera permission request was denied.",
+ Snackbar.LENGTH_SHORT)
.show();
}
}
@@ -86,43 +99,52 @@ public class MainActivity extends Activity {
private void showCameraPreview() {
// BEGIN_INCLUDE(startCamera)
- if (!isMNC()) {
- // Below Android M there is no need to check for runtime permissions
- Toast.makeText(this,
- "Requested permissions are granted at install time below M and are always "
- + "available at runtime.",
- Toast.LENGTH_SHORT).show();
+ // Check if the Camera permission has been granted
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
+ == PackageManager.PERMISSION_GRANTED) {
+ // Permission is already available, start camera preview
+ Snackbar.make(mLayout,
+ "Camera permission is available. Starting preview.",
+ Snackbar.LENGTH_SHORT).show();
startCamera();
- return;
+ } else {
+ // Permission is missing and must be requested.
+ requestCameraPermission();
}
+ // END_INCLUDE(startCamera)
+ }
- // Check if the Camera permission has been granted
- if (checkSelfPermission(Manifest.permission.CAMERA)
- != PackageManager.PERMISSION_GRANTED) {
- // Permission has not been granted and must be requested.
+ /**
+ * Requests the {@link android.Manifest.permission#CAMERA} permission.
+ * If an additional rationale should be displayed, the user has to launch the request from
+ * a SnackBar that includes additional information.
+ */
+ private void requestCameraPermission() {
+ // Permission has not been granted and must be requested.
+ 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.
+ // Display a SnackBar with a button to request the missing permission.
+ Snackbar.make(mLayout, "Camera access is required to display the camera preview.",
+ Snackbar.LENGTH_INDEFINITE).setAction("OK", new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // Request the permission
+ ActivityCompat.requestPermissions(MainActivity.this,
+ new String[]{Manifest.permission.CAMERA},
+ PERMISSION_REQUEST_CAMERA);
+ }
+ }).show();
- if (shouldShowRequestPermissionRationale(
- 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.
- Toast.makeText(this, "Camera access is required to display a camera preview.",
- Toast.LENGTH_SHORT).show();
- }
- Toast.makeText(this,
+ } else {
+ Snackbar.make(mLayout,
"Permission is not available. Requesting camera permission.",
- Toast.LENGTH_SHORT).show();
-
- // Request the permission. The result will be received in onRequestPermissionResult()
- requestPermissions(new String[]{Manifest.permission.CAMERA},
+ Snackbar.LENGTH_SHORT).show();
+ // Request the permission. The result will be received in onRequestPermissionResult().
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
PERMISSION_REQUEST_CAMERA);
- } else {
- // Permission is already available, start camera preview
- startCamera();
- Toast.makeText(this,
- "Camera permission is available. Starting preview.",
- Toast.LENGTH_SHORT).show();
}
- // END_INCLUDE(startCamera)
}
private void startCamera() {
@@ -130,7 +152,4 @@ public class MainActivity extends Activity {
startActivity(intent);
}
- public static boolean isMNC() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
- }
}