summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Lohmaier <lohmaier+LibreOffice@googlemail.com>2022-10-28 14:32:51 +0200
committerChristian Lohmaier <lohmaier+LibreOffice@googlemail.com>2022-10-30 13:21:02 +0100
commit20db0aec4929a8d3fde087355ab1450c62e352fa (patch)
treec0b6906dbd2cd61a360f3dfbb5fff02f41660210
parent93fb5b26e6ab3c3b5ab31631a735158f1de795ad (diff)
add runtime permission requests for Android 12 and later
Change-Id: I4c6cf225a838c28b0881e6b3264cbaa565ed9b6a
-rw-r--r--android/sdremote/mobile/build.gradle2
-rw-r--r--android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/activity/ComputersActivity.java108
-rw-r--r--android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/adapter/ComputersPagerAdapter.java6
-rw-r--r--android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/communication/BluetoothServersFinder.java16
4 files changed, 101 insertions, 31 deletions
diff --git a/android/sdremote/mobile/build.gradle b/android/sdremote/mobile/build.gradle
index 5629521..b5fff76 100644
--- a/android/sdremote/mobile/build.gradle
+++ b/android/sdremote/mobile/build.gradle
@@ -19,6 +19,8 @@ android {
}
dependencies {
+ // for letting the system manage the runtime permission request codes
+ implementation 'androidx.activity:activity:1.5.0'
// workaround for https://issuetracker.google.com/issues/238425626 - otherwise you get duplicate
// class errors due to mismatch between preferences and material versions, drop with next update
// of the preferences package
diff --git a/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/activity/ComputersActivity.java b/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/activity/ComputersActivity.java
index 53c9b3b..4a2e058 100644
--- a/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/activity/ComputersActivity.java
+++ b/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/activity/ComputersActivity.java
@@ -8,31 +8,41 @@
*/
package org.libreoffice.impressremote.activity;
+import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import com.google.android.material.tabs.TabLayout;
-import androidx.viewpager.widget.ViewPager;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AnimationUtils;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.content.ContextCompat;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.google.android.material.tabs.TabLayout;
+
+import org.libreoffice.impressremote.R;
import org.libreoffice.impressremote.adapter.ComputersPagerAdapter;
import org.libreoffice.impressremote.fragment.ComputersFragment.Type;
import org.libreoffice.impressremote.util.Intents;
-import org.libreoffice.impressremote.R;
public class ComputersActivity extends AppCompatActivity {
private static final int REQUEST_ENABLE_BT = 0;
private static final String SELECT_BLUETOOTH = "SELECT_BLUETOOTH";
private static final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
private static final boolean disableBTOnQuit = btAdapter != null && !btAdapter.isEnabled();
+ private static boolean haveBluetoothPermissionConnect = false;
+ private static boolean haveBluetoothPermissionScan = false;
private TabLayout tabLayout;
private static TabLayout.Tab btTab;
private static TabLayout.Tab wifiTab;
@@ -40,10 +50,49 @@ public class ComputersActivity extends AppCompatActivity {
private FloatingActionButton addFab;
private final ComputersPagerAdapter computersPagerAdapter = new ComputersPagerAdapter(getSupportFragmentManager(), this);
+ private final ActivityResultLauncher<String> requestPermissionBTConnect =
+ registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
+ // TODO add blurp about not being able to use BT stuff if not granted
+ haveBluetoothPermissionConnect = isGranted;
+ init();
+ });
+
+ // requesting the connect permission actually asks for the nearby devices permission group,
+ // requests for the scan permission are then automatically granted without a prompt, but you
+ // still have to explicitly ask for it before you can call any related methods
+ private final ActivityResultLauncher<String> requestPermissionBTScan =
+ registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
+ haveBluetoothPermissionScan = isGranted;
+ btFab.setClickable(isGranted);
+ btFab.performClick();
+ });
+
+ public static boolean getHaveBTConnect() {
+ return haveBluetoothPermissionConnect;
+ }
+
+ public static boolean getHaveBTScan() {
+ return haveBluetoothPermissionScan;
+ }
+
@Override
protected void onCreate(Bundle aSavedInstanceState) {
super.onCreate(aSavedInstanceState);
-
+ // the pre-Android 12 Bluetooth permissions have "normal" danger level and don't require
+ // asking at runtime
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || ContextCompat.checkSelfPermission(
+ this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) {
+ haveBluetoothPermissionConnect = true;
+ init();
+ // TODO: add else if (shouldShowRequestPermissionRationale(...)) {
+ // without that the user is only prompted a single time/would have to clear permissions
+ // in system settings to be prompted again.
+ } else {
+ requestPermissionBTConnect.launch(Manifest.permission.BLUETOOTH_CONNECT);
+ }
+ }
+
+ private void init() {
setContentView(R.layout.activity_computers);
Toolbar toolbar = (Toolbar) findViewById(R.id.computers_toolbar);
@@ -53,7 +102,7 @@ public class ComputersActivity extends AppCompatActivity {
// to intents filter but it shows wrong label for recent apps screen then.
assert toolbar != null;
toolbar.setTitle(R.string.title_computers);
-
+ computersPagerAdapter.reset();
computersPagerAdapter.addFragment(Type.BLUETOOTH);
computersPagerAdapter.addFragment(Type.WIFI);
@@ -69,7 +118,7 @@ public class ComputersActivity extends AppCompatActivity {
assert wifiTab != null;
wifiTab.select();
- if (btAdapter == null) {
+ if (btAdapter == null || !haveBluetoothPermissionConnect) {
computersPagerAdapter.removeFragment(Type.BLUETOOTH);
}
@@ -78,7 +127,7 @@ public class ComputersActivity extends AppCompatActivity {
@Override
public void onTabSelected(TabLayout.Tab tab) {
super.onTabSelected(tab);
- if (btAdapter != null
+ if (btAdapter != null && haveBluetoothPermissionConnect
&& tab.getPosition() == btTab.getPosition()
&& !btAdapter.isEnabled()) {
startActivityForResult(
@@ -88,13 +137,24 @@ public class ComputersActivity extends AppCompatActivity {
}
}
);
-
btFab = (FloatingActionButton) findViewById(R.id.btFab);
btFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (btAdapter != null && btAdapter.startDiscovery()) {
- btFab.setClickable(false);
+ btFab.clearAnimation();
+ if (btAdapter == null) {
+ return;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ContextCompat.checkSelfPermission(v.getContext(),
+ Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissionBTScan.launch(Manifest.permission.BLUETOOTH_SCAN);
+ return;
+ } else {
+ haveBluetoothPermissionScan = true;
+ }
+ if (btAdapter.isDiscovering()) {
+ btAdapter.cancelDiscovery();
+ } else if (btAdapter.startDiscovery()) {
// workaround, see https://code.google.com/p/android/issues/detail?id=175696#c6
// when not delayed animation won't show on pre-lollipop unless switching tabs
btFab.postDelayed(new Runnable() {
@@ -133,7 +193,7 @@ public class ComputersActivity extends AppCompatActivity {
@Override
public boolean onCreateOptionsMenu(Menu aMenu) {
getMenuInflater().inflate(R.menu.menu_action_bar_computers, aMenu);
- // ComputerFragement uses invalidateOptionsMKenu when BT-discovery broadcast is received
+ // ComputerFragment uses invalidateOptionsMenu when BT-discovery broadcast is received
toggleFab(tabLayout.getSelectedTabPosition());
return true;
}
@@ -160,20 +220,18 @@ public class ComputersActivity extends AppCompatActivity {
wifiTab.select();
return;
}
- if (btAdapter.isDiscovering()) {
- btFab.setClickable(false);
+ btFab.setClickable(true);
+ btFab.clearAnimation();
+
+ if (haveBluetoothPermissionScan && btAdapter.isDiscovering()) {
btFab.startAnimation(
AnimationUtils.loadAnimation(getApplication(), R.anim.fabalpha));
- } else {
- btFab.setClickable(true);
- if(btFab.getAnimation() != null) {
- btFab.clearAnimation();
- }
}
btFab.show();
}
});
} else {
+ btFab.setClickable(false);
btFab.hide(new FloatingActionButton.OnVisibilityChangedListener() {
@Override
public void onHidden(FloatingActionButton fab) {
@@ -186,7 +244,9 @@ public class ComputersActivity extends AppCompatActivity {
@Override
protected void onStop() {
super.onStop();
-
+ if(btTab == null) {
+ return;
+ }
SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean(SELECT_BLUETOOTH, btTab.getPosition() == tabLayout.getSelectedTabPosition());
@@ -198,7 +258,7 @@ public class ComputersActivity extends AppCompatActivity {
super.onStart();
SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);
- if (btTab.getPosition() != TabLayout.Tab.INVALID_POSITION
+ if (haveBluetoothPermissionConnect && btTab.getPosition() != TabLayout.Tab.INVALID_POSITION
&& sharedPref.getBoolean(SELECT_BLUETOOTH, btAdapter != null)) {
btTab.select();
}
@@ -207,7 +267,7 @@ public class ComputersActivity extends AppCompatActivity {
@Override
protected void onDestroy() {
super.onDestroy();
- if (isFinishing() && disableBTOnQuit) {
+ if (isFinishing() && disableBTOnQuit && haveBluetoothPermissionConnect) {
btAdapter.disable();
}
}
diff --git a/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/adapter/ComputersPagerAdapter.java b/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/adapter/ComputersPagerAdapter.java
index f07c87a..5381486 100644
--- a/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/adapter/ComputersPagerAdapter.java
+++ b/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/adapter/ComputersPagerAdapter.java
@@ -31,6 +31,12 @@ public class ComputersPagerAdapter extends FragmentPagerAdapter {
pager_size++;
}
+ public void reset() {
+ tabs[0] = null;
+ tabs[1] = null;
+ pager_size=0;
+ }
+
public void removeFragment(ComputersFragment.Type type) {
switch(type) {
case WIFI: tabs[1] = null; break;
diff --git a/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/communication/BluetoothServersFinder.java b/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/communication/BluetoothServersFinder.java
index 9806769..d15cf10 100644
--- a/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/communication/BluetoothServersFinder.java
+++ b/android/sdremote/mobile/src/main/java/org/libreoffice/impressremote/communication/BluetoothServersFinder.java
@@ -21,8 +21,10 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import org.libreoffice.impressremote.activity.ComputersActivity;
import org.libreoffice.impressremote.util.Intents;
class BluetoothServersFinder extends BroadcastReceiver implements ServersFinder {
@@ -40,7 +42,7 @@ class BluetoothServersFinder extends BroadcastReceiver implements ServersFinder
@Override
public void startSearch() {
- if (btAdapter == null) {
+ if (btAdapter == null || !ComputersActivity.getHaveBTConnect()) {
return;
}
IntentFilter aBluetoothActionsFilter = new IntentFilter();
@@ -54,10 +56,9 @@ class BluetoothServersFinder extends BroadcastReceiver implements ServersFinder
for (BluetoothDevice device : pairedDevices) {
addServer(device);
}
- } else {
- if (btAdapter.isDiscovering()) {
- return;
- }
+ return;
+ }
+ if (ComputersActivity.getHaveBTScan() && !btAdapter.isDiscovering()) {
btAdapter.startDiscovery();
}
}
@@ -123,14 +124,15 @@ class BluetoothServersFinder extends BroadcastReceiver implements ServersFinder
if (btAdapter == null) {
return;
}
-
try {
mContext.unregisterReceiver(this);
} catch (IllegalArgumentException e) {
// Receiver not registered.
// Fixed in Honeycomb: Android’s issue #6191.
}
-
+ if (!ComputersActivity.getHaveBTScan()) {
+ return;
+ }
btAdapter.cancelDiscovery();
}