mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Request notifications permission so we can target Android 13
Starting next month (September 2023) it will be required to target Android 13 to publish to the Play Store. On Android 13, we need to request the POST_NOTIFICATIONS permission. This change adds the permission as required/optional to the plugins that do create notifications. It also adds a popup to request it the first time you open the app.
This commit is contained in:
parent
37db0810aa
commit
4d2efe6b89
@ -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.SYSTEM_ALERT_WINDOW" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
|
<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.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
@ -41,7 +41,7 @@ android {
|
|||||||
compileSdk = 33
|
compileSdk = 33
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 32
|
targetSdk = 33
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
|
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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"
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
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"?>
|
<?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"
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
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>60000000</item>
|
||||||
<item>120000000</item>
|
<item>120000000</item>
|
||||||
</string-array>
|
</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_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_settings_summary">Allow controlling your media players without opening KDE Connect</string>
|
||||||
<string name="mpris_notification_key" translatable="false">mpris_notification_enabled</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="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="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_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="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_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>
|
<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>
|
||||||
@ -549,4 +551,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
|||||||
|
|
||||||
<string name="enable_udp_broadcast">Enable UDP device discovery</string>
|
<string name="enable_udp_broadcast">Enable UDP device discovery</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>
|
</resources>
|
||||||
|
@ -67,8 +67,6 @@ public class BigscreenActivity extends AppCompatActivity {
|
|||||||
new PermissionsAlertDialogFragment.Builder()
|
new PermissionsAlertDialogFragment.Builder()
|
||||||
.setTitle(plugin.getDisplayName())
|
.setTitle(plugin.getDisplayName())
|
||||||
.setMessage(R.string.bigscreen_optional_permission_explanation)
|
.setMessage(R.string.bigscreen_optional_permission_explanation)
|
||||||
.setPositiveButton(R.string.ok)
|
|
||||||
.setNegativeButton(R.string.cancel)
|
|
||||||
.setPermissions(new String[]{Manifest.permission.RECORD_AUDIO})
|
.setPermissions(new String[]{Manifest.permission.RECORD_AUDIO})
|
||||||
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
|
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
|
||||||
.create().show(getSupportFragmentManager(), null);
|
.create().show(getSupportFragmentManager(), null);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
|
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@ -120,6 +121,9 @@ public class FindMyPhonePlugin extends Plugin {
|
|||||||
intent.putExtra(FindMyPhoneActivity.EXTRA_DEVICE_ID, device.getDeviceId());
|
intent.putExtra(FindMyPhoneActivity.EXTRA_DEVICE_ID, device.getDeviceId());
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
|
if (!checkOptionalPermissions()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (powerManager.isInteractive()) {
|
if (powerManager.isInteractive()) {
|
||||||
startPlaying();
|
startPlaying();
|
||||||
showBroadcastNotification();
|
showBroadcastNotification();
|
||||||
@ -127,7 +131,6 @@ public class FindMyPhonePlugin extends Plugin {
|
|||||||
showActivityNotification();
|
showActivityNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,4 +222,19 @@ public class FindMyPhonePlugin extends Plugin {
|
|||||||
public PluginSettingsFragment getSettingsFragment(Activity activity) {
|
public PluginSettingsFragment getSettingsFragment(Activity activity) {
|
||||||
return FindMyPhoneSettingsFragment.newInstance(getPluginKey(), R.xml.findmyphoneplugin_preferences);
|
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;
|
package org.kde.kdeconnect.Plugins.MprisPlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@ -257,6 +259,14 @@ public class MprisMediaSession implements
|
|||||||
*/
|
*/
|
||||||
private void updateMediaNotification() {
|
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
|
//If the user disabled the media notification, do not show it
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
if (!prefs.getBoolean(context.getString(R.string.mpris_notification_key), true)) {
|
if (!prefs.getBoolean(context.getString(R.string.mpris_notification_key), true)) {
|
||||||
|
@ -6,15 +6,18 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.Plugins.MprisPlugin;
|
package org.kde.kdeconnect.Plugins.MprisPlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.kde.kdeconnect.NetworkPacket;
|
import org.kde.kdeconnect.NetworkPacket;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||||
@ -521,4 +524,19 @@ public class MprisPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
return false;
|
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;
|
package org.kde.kdeconnect.Plugins.PingPlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
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.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
@ -66,6 +72,15 @@ public class PingPlugin extends Plugin {
|
|||||||
id = 42; //A unique id to create only one notification
|
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);
|
NotificationManager notificationManager = ContextCompat.getSystemService(context, NotificationManager.class);
|
||||||
|
|
||||||
Notification noti = new NotificationCompat.Builder(context, NotificationHelper.Channels.DEFAULT)
|
Notification noti = new NotificationCompat.Builder(context, NotificationHelper.Channels.DEFAULT)
|
||||||
|
@ -278,8 +278,6 @@ public abstract class Plugin {
|
|||||||
return new PermissionsAlertDialogFragment.Builder()
|
return new PermissionsAlertDialogFragment.Builder()
|
||||||
.setTitle(getDisplayName())
|
.setTitle(getDisplayName())
|
||||||
.setMessage(reason)
|
.setMessage(reason)
|
||||||
.setPositiveButton(R.string.ok)
|
|
||||||
.setNegativeButton(R.string.cancel)
|
|
||||||
.setPermissions(permissions)
|
.setPermissions(permissions)
|
||||||
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
|
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
|
||||||
.create();
|
.create();
|
||||||
|
@ -6,18 +6,21 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.Plugins.ReceiveNotificationsPlugin;
|
package org.kde.kdeconnect.Plugins.ReceiveNotificationsPlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||||
import org.kde.kdeconnect.NetworkPacket;
|
import org.kde.kdeconnect.NetworkPacket;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
@ -122,4 +125,19 @@ public class ReceiveNotificationsPlugin extends Plugin {
|
|||||||
public @NonNull String[] getOutgoingPacketTypes() {
|
public @NonNull String[] getOutgoingPacketTypes() {
|
||||||
return new String[]{PACKET_TYPE_NOTIFICATION_REQUEST};
|
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
|
@Override
|
||||||
protected int getOptionalPermissionExplanation() {
|
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
|
@Override
|
||||||
@ -290,7 +294,9 @@ public class SharePlugin extends Plugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String[] getOptionalPermissions() {
|
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;
|
return ArrayUtils.EMPTY_STRING_ARRAY;
|
||||||
} else {
|
} else {
|
||||||
return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||||
|
@ -22,6 +22,8 @@ import android.widget.TextView
|
|||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
@ -192,6 +194,15 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
|||||||
else -> setContentFragment(PairingFragment())
|
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() {
|
override fun onDestroy() {
|
||||||
@ -324,25 +335,33 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
|||||||
val uri = data.data
|
val uri = data.data
|
||||||
ShareSettingsFragment.saveStorageLocationPreference(this, uri)
|
ShareSettingsFragment.saveStorageLocationPreference(this, uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> super.onActivityResult(requestCode, resultCode, data)
|
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) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
val permissionsGranted = ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)
|
val permissionsGranted = ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)
|
||||||
if (permissionsGranted) {
|
if (permissionsGranted) {
|
||||||
val i = ArrayUtils.indexOf(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isPermissionGranted(permissions, grantResults, 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) {
|
|
||||||
// To get a writeable path manually on Android 10 and later for Share and Receive Plugin.
|
// 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.
|
// Otherwise, Receiving files will keep failing until the user chooses a path manually to receive files.
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||||
startActivityForResult(intent, STORAGE_LOCATION_CONFIGURED)
|
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
|
//New permission granted, reload plugins
|
||||||
KdeConnect.getInstance().getDevice(mCurrentDevice)?.reloadPluginsFromSettings()
|
KdeConnect.getInstance().getDevice(mCurrentDevice)?.reloadPluginsFromSettings()
|
||||||
}
|
}
|
||||||
@ -370,6 +389,7 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
|||||||
const val PAIRING_REJECTED = "rejected"
|
const val PAIRING_REJECTED = "rejected"
|
||||||
const val PAIRING_PENDING = "pending"
|
const val PAIRING_PENDING = "pending"
|
||||||
const val RESULT_NEEDS_RELOAD = RESULT_FIRST_USER
|
const val RESULT_NEEDS_RELOAD = RESULT_FIRST_USER
|
||||||
|
const val RESULT_NOTIFICATIONS_ENABLED = RESULT_FIRST_USER+1
|
||||||
const val FLAG_FORCE_OVERVIEW = "forceOverview"
|
const val FLAG_FORCE_OVERVIEW = "forceOverview"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,13 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.UserInterface;
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -21,6 +24,8 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import org.kde.kdeconnect.BackgroundService;
|
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.DevicesListBinding;
|
||||||
import org.kde.kdeconnect_tp.databinding.PairingExplanationNotTrustedBinding;
|
import org.kde.kdeconnect_tp.databinding.PairingExplanationNotTrustedBinding;
|
||||||
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextBinding;
|
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextBinding;
|
||||||
|
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextNoNotificationsBinding;
|
||||||
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextNoWifiBinding;
|
import org.kde.kdeconnect_tp.databinding.PairingExplanationTextNoWifiBinding;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -52,6 +58,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
|||||||
private PairingExplanationNotTrustedBinding pairingExplanationNotTrustedBinding;
|
private PairingExplanationNotTrustedBinding pairingExplanationNotTrustedBinding;
|
||||||
private PairingExplanationTextBinding pairingExplanationTextBinding;
|
private PairingExplanationTextBinding pairingExplanationTextBinding;
|
||||||
private PairingExplanationTextNoWifiBinding pairingExplanationTextNoWifiBinding;
|
private PairingExplanationTextNoWifiBinding pairingExplanationTextNoWifiBinding;
|
||||||
|
private PairingExplanationTextNoNotificationsBinding pairingExplanationTextNoNotificationsBinding;
|
||||||
|
|
||||||
private MainActivity mActivity;
|
private MainActivity mActivity;
|
||||||
|
|
||||||
@ -59,6 +66,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
|||||||
|
|
||||||
private TextView headerText;
|
private TextView headerText;
|
||||||
private TextView noWifiHeader;
|
private TextView noWifiHeader;
|
||||||
|
private TextView noNotificationsHeader;
|
||||||
private TextView notTrustedText;
|
private TextView notTrustedText;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -69,23 +77,35 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
|||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
devicesListBinding = DevicesListBinding.inflate(inflater, container, false);
|
devicesListBinding = DevicesListBinding.inflate(inflater, container, false);
|
||||||
|
|
||||||
pairingExplanationNotTrustedBinding = PairingExplanationNotTrustedBinding.inflate(inflater);
|
pairingExplanationNotTrustedBinding = PairingExplanationNotTrustedBinding.inflate(inflater);
|
||||||
pairingExplanationTextBinding = PairingExplanationTextBinding.inflate(inflater);
|
|
||||||
pairingExplanationTextNoWifiBinding = PairingExplanationTextNoWifiBinding.inflate(inflater);
|
|
||||||
|
|
||||||
devicesListBinding.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
|
|
||||||
|
|
||||||
notTrustedText = pairingExplanationNotTrustedBinding.getRoot();
|
notTrustedText = pairingExplanationNotTrustedBinding.getRoot();
|
||||||
notTrustedText.setOnClickListener(null);
|
notTrustedText.setOnClickListener(null);
|
||||||
notTrustedText.setOnLongClickListener(null);
|
notTrustedText.setOnLongClickListener(null);
|
||||||
|
|
||||||
|
pairingExplanationTextBinding = PairingExplanationTextBinding.inflate(inflater);
|
||||||
headerText = pairingExplanationTextBinding.getRoot();
|
headerText = pairingExplanationTextBinding.getRoot();
|
||||||
headerText.setOnClickListener(null);
|
headerText.setOnClickListener(null);
|
||||||
headerText.setOnLongClickListener(null);
|
headerText.setOnLongClickListener(null);
|
||||||
|
|
||||||
|
pairingExplanationTextNoWifiBinding = PairingExplanationTextNoWifiBinding.inflate(inflater);
|
||||||
noWifiHeader = pairingExplanationTextNoWifiBinding.getRoot();
|
noWifiHeader = pairingExplanationTextNoWifiBinding.getRoot();
|
||||||
noWifiHeader.setOnClickListener(view -> startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)));
|
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.devicesList.addHeaderView(headerText);
|
||||||
|
devicesListBinding.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
|
||||||
|
|
||||||
return devicesListBinding.getRoot();
|
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(headerText);
|
||||||
devicesListBinding.devicesList.removeHeaderView(noWifiHeader);
|
devicesListBinding.devicesList.removeHeaderView(noWifiHeader);
|
||||||
devicesListBinding.devicesList.removeHeaderView(notTrustedText);
|
devicesListBinding.devicesList.removeHeaderView(notTrustedText);
|
||||||
|
devicesListBinding.devicesList.removeHeaderView(noNotificationsHeader);
|
||||||
|
|
||||||
if (someDevicesReachable || isConnectedToNonCellularNetwork) {
|
if (someDevicesReachable || isConnectedToNonCellularNetwork) {
|
||||||
if (TrustedNetworkHelper.isTrustedNetwork(getContext())) {
|
if (!hasNotificationsPermission) {
|
||||||
|
devicesListBinding.devicesList.addHeaderView(noNotificationsHeader);
|
||||||
|
} else if (TrustedNetworkHelper.isTrustedNetwork(getContext())) {
|
||||||
devicesListBinding.devicesList.addHeaderView(headerText);
|
devicesListBinding.devicesList.addHeaderView(headerText);
|
||||||
} else {
|
} else {
|
||||||
devicesListBinding.devicesList.addHeaderView(notTrustedText);
|
devicesListBinding.devicesList.addHeaderView(notTrustedText);
|
||||||
|
@ -11,6 +11,8 @@ import android.os.Bundle;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
public class PermissionsAlertDialogFragment extends AlertDialogFragment {
|
public class PermissionsAlertDialogFragment extends AlertDialogFragment {
|
||||||
private static final String KEY_PERMISSIONS = "Permissions";
|
private static final String KEY_PERMISSIONS = "Permissions";
|
||||||
private static final String KEY_REQUEST_CODE = "RequestCode";
|
private static final String KEY_REQUEST_CODE = "RequestCode";
|
||||||
@ -43,6 +45,12 @@ public class PermissionsAlertDialogFragment extends AlertDialogFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder extends AlertDialogFragment.AbstractBuilder<Builder, PermissionsAlertDialogFragment> {
|
public static class Builder extends AlertDialogFragment.AbstractBuilder<Builder, PermissionsAlertDialogFragment> {
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
setPositiveButton(R.string.ok);
|
||||||
|
setNegativeButton(R.string.cancel);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Builder getThis() {
|
public Builder getThis() {
|
||||||
return this;
|
return this;
|
||||||
|
@ -75,8 +75,6 @@ public class TrustedNetworksActivity extends AppCompatActivity {
|
|||||||
new PermissionsAlertDialogFragment.Builder()
|
new PermissionsAlertDialogFragment.Builder()
|
||||||
.setTitle(R.string.location_permission_needed_title)
|
.setTitle(R.string.location_permission_needed_title)
|
||||||
.setMessage(R.string.location_permission_needed_desc)
|
.setMessage(R.string.location_permission_needed_desc)
|
||||||
.setPositiveButton(R.string.ok)
|
|
||||||
.setNegativeButton(R.string.cancel)
|
|
||||||
.setPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION})
|
.setPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION})
|
||||||
.setRequestCode(0)
|
.setRequestCode(0)
|
||||||
.create().show(getSupportFragmentManager(), null);
|
.create().show(getSupportFragmentManager(), null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user