2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-01 06:35:09 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Albert Vaca Cintora
7394b861d2 WIP: Open settings when permissions have been denied twice 2023-08-24 01:43:58 +02:00
Albert Vaca Cintora
8a4da371c8 Request notifications permission so we can target Android 13 2023-08-23 18:55:08 +02:00
20 changed files with 270 additions and 26 deletions

View File

@@ -49,6 +49,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:icon="@mipmap/ic_launcher"

View File

@@ -41,7 +41,7 @@ android {
compileSdk = 33
defaultConfig {
minSdk = 21
targetSdk = 32
targetSdk = 33
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
buildFeatures {

View File

@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:drawablePadding="8dp"
android:paddingTop="16dp"
android:paddingBottom="12dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="@string/no_notifications"
app:drawableStartCompat="@drawable/ic_warning"
app:drawableTint="?attr/colorControlNormal">
</TextView>

View File

@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2019 Matthijs Tijink <matthijstijink@gmail.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:drawablePadding="8dp"
android:paddingTop="16dp"
android:paddingBottom="12dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="@string/no_notifications"
app:drawableStartCompat="@drawable/ic_warning"
app:drawableLeftCompat="@drawable/ic_warning">
</TextView>

View File

@@ -252,6 +252,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<item>60000000</item>
<item>120000000</item>
</string-array>
<string name="mpris_notifications_explanation">The notifications permission is needed to show remote media in the notifications drawer</string>
<string name="mpris_notification_settings_title">Show media control notification</string>
<string name="mpris_notification_settings_summary">Allow controlling your media players without opening KDE Connect</string>
<string name="mpris_notification_key" translatable="false">mpris_notification_enabled</string>
@@ -331,6 +332,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<string name="optional_permission_explanation">You need to grant extra permissions to enable all functions</string>
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="share_optional_permission_explanation">To receive files you need to allow storage access</string>
<string name="share_notifications_explanation">To see the progress when sending and receiving files you need to allow notifications</string>
<string name="telepathy_permission_explanation">To read and write SMS from your desktop you need to give permission to SMS</string>
<string name="telephony_permission_explanation">To see phone calls on the desktop you need to give permission to phone call logs and phone state</string>
<string name="telephony_optional_permission_explanation">To see a contact name instead of a phone number you need to give access to the phone\'s contacts</string>
@@ -547,4 +549,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<string name="plugin_stats">Plugin stats</string>
<string name="receive_notifications_permission_explanation">Notifications need to be allowed to receive them from other devices</string>
<string name="findmyphone_notifications_explanation">The notifications permission is needed so the phone can ring when the app is in the background</string>
<string name="no_notifications">Notifications are disabled, you won\'t receive incoming pair notifications.</string>
</resources>

View File

@@ -32,6 +32,7 @@ import androidx.lifecycle.MutableLiveData;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Backends.LoopbackBackend.LoopbackLinkProvider;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity;
@@ -80,7 +81,7 @@ public class BackgroundService extends Service {
private void registerLinkProviders() {
linkProviders.add(new LanLinkProvider(this));
// linkProviders.add(new LoopbackLinkProvider(this));
linkProviders.add(new LoopbackLinkProvider(this));
// linkProviders.add(new BluetoothLinkProvider(this));
}
@@ -267,6 +268,14 @@ public class BackgroundService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("KDE/BackgroundService", "onStartCommand");
postForegroundNotification();
if (intent != null && intent.getBooleanExtra("refresh", false)) {
onNetworkChange();
}
return Service.START_STICKY;
}
public void postForegroundNotification() {
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
@@ -274,10 +283,6 @@ public class BackgroundService extends Service {
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
}
}
if (intent != null && intent.getBooleanExtra("refresh", false)) {
onNetworkChange();
}
return Service.START_STICKY;
}
public static void Start(Context context) {

View File

@@ -67,8 +67,6 @@ public class BigscreenActivity extends AppCompatActivity {
new PermissionsAlertDialogFragment.Builder()
.setTitle(plugin.getDisplayName())
.setMessage(R.string.bigscreen_optional_permission_explanation)
.setPositiveButton(R.string.ok)
.setNegativeButton(R.string.cancel)
.setPermissions(new String[]{Manifest.permission.RECORD_AUDIO})
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
.create().show(getSupportFragmentManager(), null);

View File

@@ -6,6 +6,7 @@
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
import android.Manifest;
import android.app.Activity;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -120,6 +121,9 @@ public class FindMyPhonePlugin extends Plugin {
intent.putExtra(FindMyPhoneActivity.EXTRA_DEVICE_ID, device.getDeviceId());
context.startActivity(intent);
} else {
if (!checkOptionalPermissions()) {
return false;
}
if (powerManager.isInteractive()) {
startPlaying();
showBroadcastNotification();
@@ -127,7 +131,6 @@ public class FindMyPhonePlugin extends Plugin {
showActivityNotification();
}
}
return true;
}
@@ -219,4 +222,19 @@ public class FindMyPhonePlugin extends Plugin {
public PluginSettingsFragment getSettingsFragment(Activity activity) {
return FindMyPhoneSettingsFragment.newInstance(getPluginKey(), R.xml.findmyphoneplugin_preferences);
}
@NonNull
@Override
protected String[] getRequiredPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return new String[]{Manifest.permission.POST_NOTIFICATIONS};
} else {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
}
@Override
protected int getPermissionExplanation() {
return R.string.findmyphone_notifications_explanation;
}
}

View File

@@ -6,11 +6,13 @@
package org.kde.kdeconnect.Plugins.MprisPlugin;
import android.Manifest;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.os.Build;
@@ -257,6 +259,14 @@ public class MprisMediaSession implements
*/
private void updateMediaNotification() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
int permissionResult = ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS);
if (permissionResult != PackageManager.PERMISSION_GRANTED) {
closeMediaNotification();
return;
}
}
//If the user disabled the media notification, do not show it
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (!prefs.getBoolean(context.getString(R.string.mpris_notification_key), true)) {

View File

@@ -6,15 +6,18 @@
package org.kde.kdeconnect.Plugins.MprisPlugin;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.util.Log;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import org.apache.commons.lang3.ArrayUtils;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
@@ -521,4 +524,19 @@ public class MprisPlugin extends Plugin {
}
return false;
}
@NonNull
@Override
protected String[] getOptionalPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return new String[]{Manifest.permission.POST_NOTIFICATIONS};
} else {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
}
@Override
protected int getOptionalPermissionExplanation() {
return R.string.mpris_notifications_explanation;
}
}

View File

@@ -6,12 +6,18 @@
package org.kde.kdeconnect.Plugins.PingPlugin;
import android.Manifest;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
@@ -66,6 +72,15 @@ public class PingPlugin extends Plugin {
id = 42; //A unique id to create only one notification
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
int permissionResult = ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS);
if (permissionResult != PackageManager.PERMISSION_GRANTED) {
// If notifications are not allowed, show a toast instead of a notification
new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(context, message, Toast.LENGTH_LONG).show());
return true;
}
}
NotificationManager notificationManager = ContextCompat.getSystemService(context, NotificationManager.class);
Notification noti = new NotificationCompat.Builder(context, NotificationHelper.Channels.DEFAULT)

View File

@@ -278,8 +278,6 @@ public abstract class Plugin {
return new PermissionsAlertDialogFragment.Builder()
.setTitle(getDisplayName())
.setMessage(reason)
.setPositiveButton(R.string.ok)
.setNegativeButton(R.string.cancel)
.setPermissions(permissions)
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
.create();

View File

@@ -6,18 +6,21 @@
package org.kde.kdeconnect.Plugins.ReceiveNotificationsPlugin;
import android.Manifest;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import org.apache.commons.lang3.ArrayUtils;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
@@ -122,4 +125,19 @@ public class ReceiveNotificationsPlugin extends Plugin {
public @NonNull String[] getOutgoingPacketTypes() {
return new String[]{PACKET_TYPE_NOTIFICATION_REQUEST};
}
@NonNull
@Override
protected String[] getRequiredPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return new String[]{Manifest.permission.POST_NOTIFICATIONS};
} else {
return ArrayUtils.EMPTY_STRING_ARRAY;
}
}
@Override
protected int getPermissionExplanation() {
return R.string.receive_notifications_permission_explanation;
}
}

View File

@@ -75,7 +75,11 @@ public class SharePlugin extends Plugin {
@Override
protected int getOptionalPermissionExplanation() {
return R.string.share_optional_permission_explanation;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return R.string.share_notifications_explanation;
} else {
return R.string.share_optional_permission_explanation;
}
}
@Override
@@ -290,7 +294,9 @@ public class SharePlugin extends Plugin {
@Override
public @NonNull String[] getOptionalPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return new String[]{Manifest.permission.POST_NOTIFICATIONS};
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return ArrayUtils.EMPTY_STRING_ARRAY;
} else {
return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};

View File

@@ -22,6 +22,8 @@ import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
@@ -192,6 +194,15 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
else -> setContentFragment(PairingFragment())
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val permissionResult = ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
if (permissionResult != PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), RESULT_NOTIFICATIONS_ENABLED)
}
}
}
}
override fun onDestroy() {
@@ -324,25 +335,33 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
val uri = data.data
ShareSettingsFragment.saveStorageLocationPreference(this, uri)
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
fun isPermissionGranted(permissions: Array<String>, grantResults: IntArray, permission : String) : Boolean {
val index = ArrayUtils.indexOf(permissions, permission)
return index != ArrayUtils.INDEX_NOT_FOUND && grantResults[index] == PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
val permissionsGranted = ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)
if (permissionsGranted) {
val i = ArrayUtils.indexOf(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)
val writeStoragePermissionGranted = i != ArrayUtils.INDEX_NOT_FOUND &&
grantResults[i] == PackageManager.PERMISSION_GRANTED
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && writeStoragePermissionGranted) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isPermissionGranted(permissions, grantResults, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// To get a writeable path manually on Android 10 and later for Share and Receive Plugin.
// Otherwise, Receiving files will keep failing until the user chooses a path manually to receive files.
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, STORAGE_LOCATION_CONFIGURED)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && isPermissionGranted(permissions, grantResults, Manifest.permission.POST_NOTIFICATIONS)) {
// If PairingFragment is active, reload it
if (mCurrentDevice == null) {
setContentFragment(PairingFragment())
}
}
//New permission granted, reload plugins
KdeConnect.getInstance().getDevice(mCurrentDevice)?.reloadPluginsFromSettings()
}
@@ -370,6 +389,7 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
const val PAIRING_REJECTED = "rejected"
const val PAIRING_PENDING = "pending"
const val RESULT_NEEDS_RELOAD = RESULT_FIRST_USER
const val RESULT_NOTIFICATIONS_ENABLED = RESULT_FIRST_USER+1
const val FLAG_FORCE_OVERVIEW = "forceOverview"
}

View File

@@ -6,10 +6,13 @@
package org.kde.kdeconnect.UserInterface;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
@@ -21,6 +24,8 @@ import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import org.kde.kdeconnect.BackgroundService;
@@ -34,6 +39,7 @@ import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect_tp.databinding.DevicesListBinding;
import org.kde.kdeconnect_tp.databinding.PairingExplanationNotTrustedBinding;
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextBinding;
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextNoNotificationsBinding;
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextNoWifiBinding;
import java.util.ArrayList;
@@ -52,6 +58,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
private PairingExplanationNotTrustedBinding pairingExplanationNotTrustedBinding;
private PairingExplanationTextBinding pairingExplanationTextBinding;
private PairingExplanationTextNoWifiBinding pairingExplanationTextNoWifiBinding;
private PairingExplanationTextNoNotificationsBinding pairingExplanationTextNoNotificationsBinding;
private MainActivity mActivity;
@@ -59,6 +66,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
private TextView headerText;
private TextView noWifiHeader;
private TextView noNotificationsHeader;
private TextView notTrustedText;
@Override
@@ -69,23 +77,35 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
setHasOptionsMenu(true);
devicesListBinding = DevicesListBinding.inflate(inflater, container, false);
pairingExplanationNotTrustedBinding = PairingExplanationNotTrustedBinding.inflate(inflater);
pairingExplanationTextBinding = PairingExplanationTextBinding.inflate(inflater);
pairingExplanationTextNoWifiBinding = PairingExplanationTextNoWifiBinding.inflate(inflater);
devicesListBinding.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
notTrustedText = pairingExplanationNotTrustedBinding.getRoot();
notTrustedText.setOnClickListener(null);
notTrustedText.setOnLongClickListener(null);
pairingExplanationTextBinding = PairingExplanationTextBinding.inflate(inflater);
headerText = pairingExplanationTextBinding.getRoot();
headerText.setOnClickListener(null);
headerText.setOnLongClickListener(null);
pairingExplanationTextNoWifiBinding = PairingExplanationTextNoWifiBinding.inflate(inflater);
noWifiHeader = pairingExplanationTextNoWifiBinding.getRoot();
noWifiHeader.setOnClickListener(view -> startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)));
pairingExplanationTextNoNotificationsBinding = PairingExplanationTextNoNotificationsBinding.inflate(inflater);
noNotificationsHeader = pairingExplanationTextNoNotificationsBinding.getRoot();
noNotificationsHeader.setOnClickListener(view -> ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.POST_NOTIFICATIONS}, MainActivity.RESULT_NOTIFICATIONS_ENABLED));
noNotificationsHeader.setOnLongClickListener(view -> {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", requireContext().getPackageName(), null);
intent.setData(uri);
startActivity(intent);
return true;
});
devicesListBinding.devicesList.addHeaderView(headerText);
devicesListBinding.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
return devicesListBinding.getRoot();
}
@@ -207,12 +227,17 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
}
}
boolean hasNotificationsPermission = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
devicesListBinding.devicesList.removeHeaderView(headerText);
devicesListBinding.devicesList.removeHeaderView(noWifiHeader);
devicesListBinding.devicesList.removeHeaderView(notTrustedText);
devicesListBinding.devicesList.removeHeaderView(noNotificationsHeader);
if (someDevicesReachable || isConnectedToNonCellularNetwork) {
if (TrustedNetworkHelper.isTrustedNetwork(getContext())) {
if (!hasNotificationsPermission) {
devicesListBinding.devicesList.addHeaderView(noNotificationsHeader);
} else if (TrustedNetworkHelper.isTrustedNetwork(getContext())) {
devicesListBinding.devicesList.addHeaderView(headerText);
} else {
devicesListBinding.devicesList.addHeaderView(notTrustedText);

View File

@@ -6,12 +6,21 @@
package org.kde.kdeconnect.UserInterface;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import org.kde.kdeconnect_tp.R;
public class PermissionsAlertDialogFragment extends AlertDialogFragment {
private static final String KEY_PERMANENTLY_DENIED_PREFERENCES = "permanently_denied_permissions";
private static final String KEY_PERMISSIONS = "Permissions";
private static final String KEY_REQUEST_CODE = "RequestCode";
@@ -21,6 +30,18 @@ public class PermissionsAlertDialogFragment extends AlertDialogFragment {
public PermissionsAlertDialogFragment() {
}
public static void PermissionsDenied(Activity activity, String[] permissions) {
for (String permission : permissions) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
// The user selected "don't show again" or denied the permission twice, so the
// system permission dialog won't show again. We want to remember this to open the
// app preferences instead the next time
SharedPreferences prefs = activity.getSharedPreferences(KEY_PERMANENTLY_DENIED_PREFERENCES, Context.MODE_PRIVATE);
prefs.edit().putBoolean(permission, true).apply();
}
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -34,15 +55,38 @@ public class PermissionsAlertDialogFragment extends AlertDialogFragment {
permissions = args.getStringArray(KEY_PERMISSIONS);
requestCode = args.getInt(KEY_REQUEST_CODE, 0);
setCallback(new Callback() {
@Override
public void onPositiveButtonClicked() {
SharedPreferences prefs = getContext().getSharedPreferences(KEY_PERMANENTLY_DENIED_PREFERENCES, Context.MODE_PRIVATE);
boolean permanentlyDenied = false;
for (String permission : permissions) {
if (prefs.getBoolean(permission, false)) {
permanentlyDenied = true;
break;
}
}
if (permanentlyDenied) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getContext().getPackageName(), null);
intent.setData(uri);
startActivity(intent);
} else {
ActivityCompat.requestPermissions(requireActivity(), permissions, requestCode);
}
}
});
}
public static class Builder extends AlertDialogFragment.AbstractBuilder<Builder, PermissionsAlertDialogFragment> {
public Builder() {
setPositiveButton(R.string.ok);
setNegativeButton(R.string.cancel);
}
@Override
public Builder getThis() {
return this;

View File

@@ -75,8 +75,6 @@ public class TrustedNetworksActivity extends AppCompatActivity {
new PermissionsAlertDialogFragment.Builder()
.setTitle(R.string.location_permission_needed_title)
.setMessage(R.string.location_permission_needed_desc)
.setPositiveButton(R.string.ok)
.setNegativeButton(R.string.cancel)
.setPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION})
.setRequestCode(0)
.create().show(getSupportFragmentManager(), null);