Sync mnc-dev sample prebuilts

Syncing to //developers/samples/android commmit 2be5f5ca32.

Change-Id: Ia08c63655bd122c7fbb0cc3a50eec95d8edcb3f1
This commit is contained in:
Trevor Johns
2015-08-17 09:37:47 -07:00
parent a56a634166
commit c8e94bf583
28 changed files with 734 additions and 323 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -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;
}
}

View File

@@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -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>

View File

@@ -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,67 +32,121 @@ 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;
}
/**
* When app comes on screen, check if BLE Advertisements are running, set switch accordingly,
* and register the Receiver to be notified if Advertising fails.
*/
@Override
public void onStop() {
super.onStop();
public void onResume() {
super.onResume();
if(mAdvertiseCallback != null){
stopAdvertising();
if (AdvertiserService.running) {
mSwitch.setChecked(true);
} else {
mSwitch.setChecked(false);
}
IntentFilter failureFilter = new IntentFilter(AdvertiserService.ADVERTISING_FAILED);
getActivity().registerReceiver(advertisingFailureReceiver, failureFilter);
}
/**
* 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)
*/
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(advertisingFailureReceiver);
}
/**
* Returns Intent addressed to the {@code AdvertiserService} class.
*/
private static Intent getServiceIntent(Context c) {
return new Intent(c, AdvertiserService.class);
}
/**
* Called when switch is toggled - starts or stops advertising.
*
* @param view is the Switch View object
*/
public void onSwitchClicked(View view) {
@Override
public void onClick(View v) {
// Is the toggle on?
boolean on = ((Switch) view).isChecked();
boolean on = ((Switch) v).isChecked();
if (on) {
startAdvertising();
@@ -102,105 +156,20 @@ public class AdvertiserFragment extends Fragment {
}
/**
* Starts BLE Advertising.
* Starts BLE Advertising by starting {@code AdvertiserService}.
*/
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();
}
Context c = getActivity();
c.startService(getServiceIntent(c));
}
/**
* Stops BLE Advertising.
* Stops BLE Advertising by stopping {@code AdvertiserService}.
*/
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();
}
Context c = getActivity();
c.stopService(getServiceIntent(c));
mSwitch.setChecked(false);
}
/**
* 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);
return dataBuilder.build();
}
/**
* Returns an AdvertiseSettings object set to use low power (to help preserve battery life).
*/
private AdvertiseSettings buildAdvertiseSettings() {
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
return settingsBuilder.build();
}
/**
* Custom callback after Advertising succeeds or fails to start.
*/
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.
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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()));

View File

@@ -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());

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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.
]]>

View File

@@ -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>

View File

@@ -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,62 +117,122 @@ 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.");
// 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();
}
// Request Camera permission
requestPermissions(new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
// 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.
// 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 {
// Camera permission has not been granted yet. Request it directly.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA);
}
// END_INCLUDE(camera_permission_request)
}
/**
* Called when the 'show camera' button is clicked.
* Callback is defined in resource layout definition.
*/
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();
}
// For example, if the request has been denied previously.
Log.i(TAG,
"Displaying contacts permission rationale to provide additional context.");
// contact permissions has not been granted (read and write contacts). Request them.
requestPermissions(PERMISSIONS_CONTACT, REQUEST_CONTACTS);
// 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();

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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");
}
}

View File

@@ -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.

View File

@@ -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>

View File

@@ -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.

View File

@@ -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,51 +99,57 @@ 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();
startCamera();
return;
}
// 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.
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,
"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},
PERMISSION_REQUEST_CAMERA);
} else {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
// Permission is already available, start camera preview
startCamera();
Toast.makeText(this,
Snackbar.make(mLayout,
"Camera permission is available. Starting preview.",
Toast.LENGTH_SHORT).show();
Snackbar.LENGTH_SHORT).show();
startCamera();
} else {
// Permission is missing and must be requested.
requestCameraPermission();
}
// END_INCLUDE(startCamera)
}
/**
* 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();
} else {
Snackbar.make(mLayout,
"Permission is not available. Requesting camera permission.",
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);
}
}
private void startCamera() {
Intent intent = new Intent(this, CameraPreviewActivity.class);
startActivity(intent);
}
public static boolean isMNC() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
}