mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-09-01 06:35:09 +00:00
Compare commits
2 Commits
work/requi
...
work/open-
Author | SHA1 | Date | |
---|---|---|---|
|
7394b861d2 | ||
|
8a4da371c8 |
@@ -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"
|
||||
|
@@ -41,7 +41,7 @@ android {
|
||||
compileSdk = 33
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
targetSdk = 32
|
||||
targetSdk = 33
|
||||
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
|
||||
}
|
||||
buildFeatures {
|
||||
|
@@ -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"
|
||||
|
25
res/layout-v23/pairing_explanation_text_no_notifications.xml
Normal file
25
res/layout-v23/pairing_explanation_text_no_notifications.xml
Normal 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>
|
@@ -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"
|
||||
|
25
res/layout/pairing_explanation_text_no_notifications.xml
Normal file
25
res/layout/pairing_explanation_text_no_notifications.xml
Normal 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>
|
@@ -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>
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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)) {
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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};
|
||||
|
@@ -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"
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user