diff --git a/drip-android-observer-master/Android/app/build.gradle b/drip-android-observer-master/Android/app/build.gradle index af1ced4baba124475f4a361b626b33dc03eabe1c..dd176fad801356facb8cbafe85989c228184ff66 100644 --- a/drip-android-observer-master/Android/app/build.gradle +++ b/drip-android-observer-master/Android/app/build.gradle @@ -20,12 +20,15 @@ android { versionName "${versionMajor}.${versionMinor}.${versionPatch}" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + buildTypes { debug { versionNameSuffix ".debug" resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix}" } release { + //signingConfig signingConfigs.release + //debuggable false resValue "string", "app_version", "${defaultConfig.versionName}" minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' diff --git a/drip-android-observer-master/Android/app/src/main/AndroidManifest.xml b/drip-android-observer-master/Android/app/src/main/AndroidManifest.xml index 720208927b4bc07c334206d49ea4ced5ce1020c0..729e8bcd701e0b3b080544fdea17309e369a881a 100644 --- a/drip-android-observer-master/Android/app/src/main/AndroidManifest.xml +++ b/drip-android-observer-master/Android/app/src/main/AndroidManifest.xml @@ -2,10 +2,17 @@ <manifest 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" /> + <!-- Request legacy Bluetooth permissions on older devices. --> + <uses-permission android:name="android.permission.BLUETOOTH" + android:maxSdkVersion="30"/> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" + android:maxSdkVersion="30"/> + <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/> + <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/Constants.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/Constants.java index b26ddd0b78ca112841430b13715d71f762216ab1..6ee51171515484f305d7cf21718586e95df7f222 100644 --- a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/Constants.java +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/Constants.java @@ -7,9 +7,10 @@ package org.dripdronescanner.android; public class Constants { - public static final int REQUEST_ENABLE_BT = 1; + public static final int REQUEST_BT_CONNECT = 1; public static final int FINE_LOCATION_PERMISSION_REQUEST_CODE = 2; public static final int REQUEST_ENABLE_WIFI = 3; + public static final int REQUEST_BT_SCAN = 4; public static final String DELIM = ","; diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftMapView.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftMapView.java index 0fcab575c979fb4e6737384cd1bdb55b0c10dbcd..72f5f06c19668266f0f8ae000c29d618bcaf3c6d 100644 --- a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftMapView.java +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/AircraftMapView.java @@ -11,6 +11,8 @@ import android.view.ViewGroup; import android.util.Log; import android.widget.ImageButton; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.core.app.ActivityCompat; import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.Fragment; @@ -18,6 +20,8 @@ import androidx.annotation.Nullable; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; +import org.dripdronescanner.android.Constants; +import org.dripdronescanner.android.PermissionUtils; import org.dripdronescanner.android.R; import org.dripdronescanner.android.data.AircraftObject; import org.dripdronescanner.android.data.LocationData; @@ -87,15 +91,12 @@ public class AircraftMapView extends Fragment { if (getActivity() == null) return; + map.setMultiTouchControls(true); + map.getController().setZoom(DESIRED_ZOOM); + if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - // TODO: Consider calling ActivityCompat#requestPermissions - // to request the missing permissions, and then overriding - // public void onRequestPermissionsResult(int requestCode, String[] permissions, - // int[] grantResults) - // to handle the case where the user grants the permission. See the documentation - // for ActivityCompat#requestPermissions for more details. - Log.e("XX", "##################### can't make the right permissions"); + requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION); return; } @@ -103,9 +104,6 @@ public class AircraftMapView extends Fragment { overlay.enableMyLocation(); overlay.enableFollowLocation(); map.getOverlays().add(overlay); - - map.setMultiTouchControls(true); - map.getController().setZoom(DESIRED_ZOOM); }); ImageButton centerMapButton = v.findViewById(R.id.ic_center_map); @@ -300,4 +298,21 @@ public class AircraftMapView extends Fragment { } } } + + private final ActivityResultLauncher<String> requestPermissionLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) { + overlay = new MyLocationNewOverlay(new GpsMyLocationProvider(requireContext()), map); + overlay.enableMyLocation(); + overlay.enableFollowLocation(); + map.getOverlays().add(overlay); + } else { + //showErrorText(R.string.permission_required_toast); + // Explain to the user that the feature is unavailable because the + // feature requires a permission that the user has denied. At the + // same time, respect the user's decision. Don't link to system + // settings in an effort to convince the user to change their + // decision. + } + }); } \ No newline at end of file diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DebugActivity.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DebugActivity.java index c12d40e3ca86feac186605805ade60f7e1c29f60..04fbd610e8f2090cc9c0ab0c74d02d80d679e7be 100644 --- a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DebugActivity.java +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/app/DebugActivity.java @@ -28,6 +28,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.fragment.app.FragmentTransaction; @@ -91,7 +92,7 @@ public class DebugActivity extends AppCompatActivity { return true; } - private void createSettingsItem(Menu menu){ + private void createSettingsItem(Menu menu) { DebugActivity current = this; MenuItem settings = menu.findItem(R.id.settings); settings.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @@ -111,7 +112,7 @@ public class DebugActivity extends AppCompatActivity { return; BluetoothAdapter bluetoothAdapter = ((android.bluetooth.BluetoothManager) object).getAdapter(); - if(bluetoothAdapter != null) { + if (bluetoothAdapter != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && bluetoothAdapter.isLeCodedPhySupported()) { menu.findItem(R.id.coded_phy).setTitle(R.string.coded_phy_supported); } @@ -127,6 +128,7 @@ public class DebugActivity extends AppCompatActivity { menu.findItem(R.id.wifi_nan).setTitle(R.string.nan_supported); } } + @TargetApi(Build.VERSION_CODES.M) private void checkWiFiSupport(Menu menu) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -188,6 +190,12 @@ public class DebugActivity extends AppCompatActivity { } private void createNewLogfile() { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + requestBluetoothConnectPermission(Constants.REQUEST_BT_CONNECT); + } + return; + } loggerFile = getLoggerFileDir(btScanner.getBluetoothAdapter().getName()); try { @@ -235,7 +243,7 @@ public class DebugActivity extends AppCompatActivity { if (!bluetoothAdapter.isEnabled()) { // Prompt user to turn on Bluetooth (logic continues in onActivityResult()). Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT); + startActivityForResult(enableBtIntent, Constants.REQUEST_BT_CONNECT); } else { bluetoothOn = true; // Check permission @@ -288,6 +296,12 @@ public class DebugActivity extends AppCompatActivity { mModel.getAllAircraft().observe(this, listObserver); + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + requestBluetoothScanPermission(Constants.REQUEST_BT_SCAN); + } + return; + } btScanner.startScan(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -307,7 +321,7 @@ public class DebugActivity extends AppCompatActivity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == Constants.REQUEST_ENABLE_BT) { + if (requestCode == Constants.REQUEST_BT_CONNECT) { if (resultCode == RESULT_OK) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { @@ -375,9 +389,28 @@ public class DebugActivity extends AppCompatActivity { Manifest.permission.ACCESS_FINE_LOCATION, false); } + @RequiresApi(api = Build.VERSION_CODES.S) + public void requestBluetoothConnectPermission(int requestCode) { + Log.d(TAG, "requestBluetoothConnectPermission: request permission"); + + // Bluetooth permission has not been granted yet, request it. + PermissionUtils.requestPermission(this, requestCode, + Manifest.permission.BLUETOOTH_CONNECT, false); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + public void requestBluetoothScanPermission(int requestCode) { + Log.d(TAG, "requestBluetoothConnectPermission: request permission"); + + // Bluetooth permission has not been granted yet, request it. + PermissionUtils.requestPermission(this, requestCode, + Manifest.permission.BLUETOOTH_SCAN, false); + } + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == Constants.FINE_LOCATION_PERMISSION_REQUEST_CODE) { Log.d(TAG, "onRequestPermissionsResult: back from request FINE_LOCATION"); if (PermissionUtils.isPermissionGranted(permissions, grantResults, @@ -387,6 +420,22 @@ public class DebugActivity extends AppCompatActivity { showErrorText(R.string.permission_required_toast); } + } else if (requestCode == Constants.REQUEST_BT_CONNECT) { + Log.d(TAG, "onRequestPermissionsResult: back from request BLUETOOTH_CONNECT"); + if (PermissionUtils.isPermissionGranted(permissions, grantResults, + Manifest.permission.BLUETOOTH_CONNECT)) { + initialize(); + } else { + showErrorText(R.string.bluetooth_connect_permission_required_toast); + } + } else if (requestCode == Constants.REQUEST_BT_SCAN) { + Log.d(TAG, "onRequestPermissionsResult: back from request BLUETOOTH_SCAN"); + if (PermissionUtils.isPermissionGranted(permissions, grantResults, + Manifest.permission.BLUETOOTH_SCAN)) { + initialize(); + } else { + showErrorText(R.string.bluetooth_scan_permission_required_toast); + } } } } diff --git a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/BluetoothScanner.java b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/BluetoothScanner.java index a5541917a4889bd79d39eb39ad4b9859621cd7e7..5c90c880a595d7e391d25ea468c9c8b5852c738a 100644 --- a/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/BluetoothScanner.java +++ b/drip-android-observer-master/Android/app/src/main/java/org/dripdronescanner/android/network/BluetoothScanner.java @@ -6,6 +6,7 @@ */ package org.dripdronescanner.android.network; +import android.Manifest; import android.annotation.TargetApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -16,10 +17,16 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; +import android.content.pm.PackageManager; import android.os.Build; import android.os.ParcelUuid; import android.util.Log; +import androidx.annotation.RequiresApi; +import androidx.core.app.ActivityCompat; + +import org.dripdronescanner.android.PermissionUtils; +import org.dripdronescanner.android.app.DebugActivity; import org.dripdronescanner.android.log.LogEntry; import org.dripdronescanner.android.log.LogMessageEntry; import org.dripdronescanner.android.log.LogWriter; @@ -47,7 +54,9 @@ public class BluetoothScanner { bluetoothAdapter = ((android.bluetooth.BluetoothManager) object).getAdapter(); } - public void setLogger(LogWriter logger) { this.logger = logger; } + public void setLogger(LogWriter logger) { + this.logger = logger; + } private static String dumpBytes(byte[] bytes) { return LogEntry.toHexString(bytes, bytes.length); @@ -72,7 +81,7 @@ public class BluetoothScanner { addr, advertiseFlags, rssi, bytes != null ? bytes.length : -1); String transportType = "BT4"; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && bluetoothAdapter.isLeCodedPhySupported()) { + if (bluetoothAdapter.isLeCodedPhySupported()) { if (result.getPrimaryPhy() == BluetoothDevice.PHY_LE_CODED) transportType = "BT5"; } @@ -110,7 +119,6 @@ public class BluetoothScanner { private static final ParcelUuid SERVICE_pUUID = new ParcelUuid(SERVICE_UUID); private static final byte[] OPEN_DRONE_ID_AD_CODE = new byte[]{(byte) 0x0D}; - @TargetApi(Build.VERSION_CODES.O) public void startScan() { if (bluetoothAdapter == null) return; @@ -124,24 +132,27 @@ public class BluetoothScanner { scanFilters.add(builder.build()); ScanSettings scanSettings = new ScanSettings.Builder() - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .build(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && - bluetoothAdapter.isLeCodedPhySupported() && - bluetoothAdapter.isLeExtendedAdvertisingSupported()) { + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .build(); + if (bluetoothAdapter.isLeCodedPhySupported() && bluetoothAdapter.isLeExtendedAdvertisingSupported()) { // Enable scanning also for devices advertising on an LE Coded PHY S2 or S8 scanSettings = new ScanSettings.Builder() - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .setLegacy(false) - .setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED) - .build(); + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .setLegacy(false) + .setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED) + .build(); + } + if (ActivityCompat.checkSelfPermission(con, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { + return; } - bluetoothLeScanner.startScan(scanFilters, scanSettings, scanCallback); } public void stopScan() { if (bluetoothLeScanner != null && scanCallback != null) { + if (ActivityCompat.checkSelfPermission(con, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { + return; + } bluetoothLeScanner.stopScan(scanCallback); } } diff --git a/drip-android-observer-master/Android/app/src/main/res/values/strings.xml b/drip-android-observer-master/Android/app/src/main/res/values/strings.xml index 5346aa24be9c0d454185722b86eb3e57b99460e5..990009d6f4a9d03f3dfde93324e0af62baef7143 100644 --- a/drip-android-observer-master/Android/app/src/main/res/values/strings.xml +++ b/drip-android-observer-master/Android/app/src/main/res/values/strings.xml @@ -41,6 +41,8 @@ <string name="permission_rationale_location">Access to the location service is required to demonstrate the \'my location\' feature, which shows your current location on the map.</string> <string name="location_permission_denied">This sample requires location permission to enable the \'my location\' layer. Please try again and grant access to use the location.\nIf the permission has been permanently denied, it can be enabled from the System Settings > Apps > \'Google Maps API Demos\'.</string> <string name="permission_required_toast">Location permission is required for Bluetooth advertising scanning.</string> + <string name="bluetooth_connect_permission_required_toast">Bluetooth connect permission is required for Bluetooth connectivity.</string> + <string name="bluetooth_scan_permission_required_toast">Bluetooth scan permission is required for Bluetooth connectivity.</string> <string name="drone_icon_content_description">Drone icon</string>