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
This commit is contained in:
parent
a74a76b1da
commit
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.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>
|
||||||
@ -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="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>
|
</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