Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration to Android 12 #54

Merged
merged 5 commits into from
Oct 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/deploy-to-play-store.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Deploy to Play Store Internal
on:
workflow_dispatch:
jobs:
deployAabToGooglePlayInternal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- shell: bash
env:
# The following env variables are used by signing configuration in sample/build.gradle
KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
KEYSTORE_KEY_PSWD: ${{ secrets.KEYSTORE_KEY_PSWD }}
KEYSTORE_PSWD: ${{ secrets.KEYSTORE_PSWD }}
# The script decodes keystore (required by sample/build.gradle) and fastlane-api.json
# needed by fastlane (see fastlane/Appfile).
run: |
echo "${{ secrets.KEYSTORE_FILE }}" > keystore.asc
gpg -d --passphrase "${{ secrets.KEYSTORE_FILE_PSWD }}" --batch keystore.asc > keystore
echo "${{ secrets.API_KEY_FILE }}" > fastlane-api.json.asc
gpg -d --passphrase "${{ secrets.API_KEY_FILE_PSWD }}" --batch fastlane-api.json.asc > fastlane-api.json
fastlane deployInternal
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source "https://rubygems.org"

gem "fastlane"
29 changes: 20 additions & 9 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
apply plugin: 'com.android.application'
apply from: rootProject.file("gradle/git-tag-version.gradle")

android {
compileSdkVersion 30
buildToolsVersion '30.0.3'
compileSdkVersion 31

defaultConfig {
applicationId "no.nordicsemi.android.nrfblinky"
minSdkVersion 18
targetSdkVersion 30
versionCode 14
versionName "2.5.1"
targetSdkVersion 31
versionCode getVersionCodeFromTags()
versionName getVersionNameFromTags()
resConfigs "en"

vectorDrawables.useSupportLibrary = true
}

signingConfigs {
release {
storeFile file('../keystore')
storePassword System.env.KEYSTORE_PSWD
keyAlias System.env.KEYSTORE_ALIAS
keyPassword System.env.KEYSTORE_KEY_PSWD
}
}

buildFeatures {
viewBinding true
}

buildTypes {
debug {
minifyEnabled false
Expand All @@ -23,11 +36,9 @@ android {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
buildFeatures {
viewBinding true
}
}

dependencies {
Expand All @@ -40,7 +51,7 @@ dependencies {
implementation 'com.google.android.material:material:1.4.0'

// Brings the new BluetoothLeScanner API to older platforms
implementation 'no.nordicsemi.android.support.v18:scanner:1.5.1'
implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'

// Log Bluetooth LE events in nRF Logger
implementation 'no.nordicsemi.android:log:2.3.0'
Expand Down
52 changes: 48 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,59 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="no.nordicsemi.android.LOG"/>
<!--
Bluetooth permission is required on Android 4.3 - 11 in order to communicate with
Bluetooth LE devices.
-->
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30"/>
<!--
Bluetooth Admin permission is required on Android 4.3 - 11 in order to scan for
Bluetooth LE devices.
-->
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30"/>
<!--
Location permission is required from Android 6 to 11 to be able to scan for advertising
Bluetooth LE devices. Some BLE devices, called beacons, may be used to position the phone.
This is to ensure that the user agrees to do so.
This app does not use this location information in any way.
Since Android 10 ACCESS_FINE_LOCATION is required and for that, the COARSE one also needs
to be specified.
-->
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30"/>
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30"/>
<!--
On Android 12 new set of Bluetooth permissions were added. BLUETOOTH_SCAN permission is
needed to scan for Bluetooth LE devices. The "never for location" flag is set, as we are not
interested in finding the user location, and we are OK with the fact, that beacons info
will be removed from the scan results.
-->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<!--
This permission is required in order to connect to a Bluetooth LE device on Android 12
onwards.
-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true"/>

<queries>
<!-- Allow logging in nRF Logger. -->
<package android:name="no.nordicsemi.android.log" />
</queries>

<application
android:name=".BlinkyApplication"
android:allowBackup="true"
Expand Down
161 changes: 118 additions & 43 deletions app/src/main/java/no/nordicsemi/android/blinky/ScannerActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
Expand All @@ -55,11 +56,6 @@ public class ScannerActivity extends AppCompatActivity implements DevicesAdapter
private ScannerViewModel scannerViewModel;
private ActivityScannerBinding binding;

private final ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
isGranted -> scannerViewModel.refresh()
);

@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -86,24 +82,46 @@ protected void onCreate(@Nullable final Bundle savedInstanceState) {
adapter.setOnItemClickListener(this);
recyclerView.setAdapter(adapter);

// Set up permission request launcher
final ActivityResultLauncher<String> requestPermission =
registerForActivityResult(new ActivityResultContracts.RequestPermission(),
result -> scannerViewModel.refresh()
);
final ActivityResultLauncher<String[]> requestPermissions =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(),
result -> scannerViewModel.refresh()
);

// Configure views
binding.noDevices.actionEnableLocation.setOnClickListener(v -> {
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
});
binding.bluetoothOff.actionEnableBluetooth.setOnClickListener(v -> {
final Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableIntent);
});
binding.noDevices.actionEnableLocation.setOnClickListener(v -> openLocationSettings());
binding.bluetoothOff.actionEnableBluetooth.setOnClickListener(v -> requestBluetoothEnabled());
binding.noLocationPermission.actionGrantLocationPermission.setOnClickListener(v -> {
Utils.markLocationPermissionRequested(this);
requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION);
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION))
Utils.markLocationPermissionRequested(this);
requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION);
});
binding.noLocationPermission.actionPermissionSettings.setOnClickListener(v -> {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", getPackageName(), null));
startActivity(intent);
Utils.clearLocationPermissionRequested(this);
openPermissionSettings();
});

if (Utils.isSorAbove()) {
binding.noBluetoothPermission.actionGrantBluetoothPermission.setOnClickListener(v -> {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.BLUETOOTH_SCAN)) {
Utils.markBluetoothScanPermissionRequested(this);
}
requestPermissions.launch(new String[] {
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
});
});
binding.noBluetoothPermission.actionPermissionSettings.setOnClickListener(v -> {
Utils.clearBluetoothPermissionRequested(this);
openPermissionSettings();
});
}
}

@Override
Expand All @@ -119,7 +137,7 @@ protected void onStop() {
}

@Override
public boolean onCreateOptionsMenu(final Menu menu) {
public boolean onCreateOptionsMenu(@NonNull final Menu menu) {
getMenuInflater().inflate(R.menu.filter, menu);
menu.findItem(R.id.filter_uuid).setChecked(scannerViewModel.isUuidFilterEnabled());
menu.findItem(R.id.filter_nearby).setChecked(scannerViewModel.isNearbyFilterEnabled());
Expand Down Expand Up @@ -149,44 +167,68 @@ public void onItemClick(@NonNull final DiscoveredBluetoothDevice device) {
}

/**
* Start scanning for Bluetooth devices or displays a message based on the scanner state.
* Starts scanning for Bluetooth LE devices or displays a message based on the scanner state.
*/
private void startScan(@NonNull final ScannerStateLiveData state) {
// First, check the Location permission. This is required on Marshmallow onwards in order
// to scan for Bluetooth LE devices.
if (Utils.isLocationPermissionsGranted(this)) {
binding.noLocationPermission.container.setVisibility(View.GONE);
// First, check the Location permission.
// This is required since Marshmallow up until Android 11 in order to scan for Bluetooth LE
// devices.
if (!Utils.isLocationPermissionRequired() ||
Utils.isLocationPermissionGranted(this)) {
binding.noLocationPermission.getRoot().setVisibility(View.GONE);

// On Android 12+ a new BLUETOOTH_SCAN and BLUETOOTH_CONNECT permissions need to be
// requested.
//
// Note: This has to be done before asking user to enable Bluetooth, as
// sending BluetoothAdapter.ACTION_REQUEST_ENABLE intent requires
// BLUETOOTH_CONNECT permission.
if (!Utils.isSorAbove() || Utils.isBluetoothScanPermissionGranted(this)) {
binding.noBluetoothPermission.getRoot().setVisibility(View.GONE);

// Bluetooth must be enabled.
if (state.isBluetoothEnabled()) {
binding.bluetoothOff.container.setVisibility(View.GONE);
// Bluetooth must be enabled
if (state.isBluetoothEnabled()) {
binding.bluetoothOff.getRoot().setVisibility(View.GONE);

// We are now OK to start scanning.
scannerViewModel.startScan();
binding.stateScanning.setVisibility(View.VISIBLE);
// We are now OK to start scanning
scannerViewModel.startScan();
binding.stateScanning.setVisibility(View.VISIBLE);

if (!state.hasRecords()) {
binding.noDevices.container.setVisibility(View.VISIBLE);
if (!state.hasRecords()) {
binding.noDevices.getRoot().setVisibility(View.VISIBLE);

if (!Utils.isLocationRequired(this) || Utils.isLocationEnabled(this)) {
binding.noDevices.noLocation.setVisibility(View.INVISIBLE);
if (!Utils.isLocationRequired(this) ||
Utils.isLocationEnabled(this)) {
binding.noDevices.noLocation.setVisibility(View.INVISIBLE);
} else {
binding.noDevices.noLocation.setVisibility(View.VISIBLE);
}
} else {
binding.noDevices.noLocation.setVisibility(View.VISIBLE);
binding.noDevices.getRoot().setVisibility(View.GONE);
}
} else {
binding.noDevices.container.setVisibility(View.GONE);
binding.bluetoothOff.getRoot().setVisibility(View.VISIBLE);
binding.stateScanning.setVisibility(View.INVISIBLE);
binding.noDevices.getRoot().setVisibility(View.GONE);
binding.noBluetoothPermission.getRoot().setVisibility(View.GONE);
clear();
}
} else {
binding.bluetoothOff.container.setVisibility(View.VISIBLE);
binding.noBluetoothPermission.getRoot().setVisibility(View.VISIBLE);
binding.bluetoothOff.getRoot().setVisibility(View.GONE);
binding.stateScanning.setVisibility(View.INVISIBLE);
binding.noDevices.container.setVisibility(View.GONE);
clear();
binding.noDevices.getRoot().setVisibility(View.GONE);

final boolean deniedForever = Utils.isBluetoothScanPermissionDeniedForever(this);
binding.noBluetoothPermission.actionGrantBluetoothPermission.setVisibility(deniedForever ? View.GONE : View.VISIBLE);
binding.noBluetoothPermission.actionPermissionSettings.setVisibility(deniedForever ? View.VISIBLE : View.GONE);
}
} else {
binding.noLocationPermission.container.setVisibility(View.VISIBLE);
binding.bluetoothOff.container.setVisibility(View.GONE);
binding.noLocationPermission.getRoot().setVisibility(View.VISIBLE);
binding.noBluetoothPermission.getRoot().setVisibility(View.GONE);
binding.bluetoothOff.getRoot().setVisibility(View.GONE);
binding.stateScanning.setVisibility(View.INVISIBLE);
binding.noDevices.container.setVisibility(View.GONE);
binding.noDevices.getRoot().setVisibility(View.GONE);

final boolean deniedForever = Utils.isLocationPermissionDeniedForever(this);
binding.noLocationPermission.actionGrantLocationPermission.setVisibility(deniedForever ? View.GONE : View.VISIBLE);
Expand All @@ -195,7 +237,7 @@ private void startScan(@NonNull final ScannerStateLiveData state) {
}

/**
* stop scanning for bluetooth devices.
* Stops scanning for Bluetooth LE devices.
*/
private void stopScan() {
scannerViewModel.stopScan();
Expand All @@ -208,4 +250,37 @@ private void clear() {
scannerViewModel.getDevices().clear();
scannerViewModel.getScannerState().clearRecords();
}

/**
* Opens application settings in Android Settings app.
*/
private void openPermissionSettings() {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", getPackageName(), null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

/**
* Opens Location settings.
*/
private void openLocationSettings() {
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

/**
* Shows a prompt to the user to enable Bluetooth on the device.
*
* @implSpec On Android 12+ BLUETOOTH_CONNECT permission needs to be granted before calling
* this method. Otherwise, the app would crash with {@link SecurityException}.
* @see BluetoothAdapter#ACTION_REQUEST_ENABLE
*/
private void requestBluetoothEnabled() {
if (Utils.isBluetoothConnectPermissionGranted(this)) {
final Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(enableIntent);
}
}
}
Loading