mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-31 14:15:14 +00:00
Compare commits
135 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
deee9cc2e2 | ||
|
a107796ec2 | ||
|
c32e00bb47 | ||
|
ba98e21d40 | ||
|
551b089d9a | ||
|
b9e9d66fa5 | ||
|
107611f8d2 | ||
|
d0fc68054f | ||
|
9f15d4b3aa | ||
|
a149100d74 | ||
|
7332b355a4 | ||
|
9cc8051d00 | ||
|
78050a158a | ||
|
a5057df1c8 | ||
|
b856bdbb0b | ||
|
8cb9a1809d | ||
|
0ffd9cd9dc | ||
|
725b3f1d6a | ||
|
63d1d15c4f | ||
|
592b8a74a7 | ||
|
38e713f489 | ||
|
bb8649d869 | ||
|
2e82673bbf | ||
|
7ee33de646 | ||
|
65a71696d5 | ||
|
e2aa79a252 | ||
|
1ffcaf076e | ||
|
854b2a1c9f | ||
|
8fb545d620 | ||
|
db615b82df | ||
|
ee7d4a1f05 | ||
|
409ef6b579 | ||
|
2c0a9d262e | ||
|
2f10f1d0f2 | ||
|
9f788da478 | ||
|
ec952b49d9 | ||
|
45da75f331 | ||
|
283956c107 | ||
|
d605977b91 | ||
|
80cf238354 | ||
|
8ee135b018 | ||
|
2343dbf144 | ||
|
3f53180b1d | ||
|
0f7af315f5 | ||
|
7ebac70d47 | ||
|
9037647281 | ||
|
9373587268 | ||
|
0771415d8f | ||
|
fb0e2cc01d | ||
|
584fb78bb7 | ||
|
ae12e3e6fc | ||
|
8be80cae3e | ||
|
d41ed4d911 | ||
|
6f783b54a5 | ||
|
1f6d1ea544 | ||
|
27737c46f2 | ||
|
6b99fe7e7c | ||
|
6e59a6241a | ||
|
7506d32cef | ||
|
c35dc4928c | ||
|
6070276489 | ||
|
9685f7e69a | ||
|
69495136da | ||
|
5f18cb571d | ||
|
ab1b1f7ecc | ||
|
3f8170eae8 | ||
|
eb6784b626 | ||
|
1beb8d4581 | ||
|
16067d7523 | ||
|
dea0bb4e1f | ||
|
c9fb81363d | ||
|
35e8ea0c4c | ||
|
7c5c7933c9 | ||
|
0dfa44aeac | ||
|
dd527f661c | ||
|
ed89fb43ed | ||
|
aed2b64416 | ||
|
4bdbb8f74a | ||
|
155ebf4fb2 | ||
|
46ad0c62ba | ||
|
df0f2d651c | ||
|
167e2c7176 | ||
|
906326f837 | ||
|
3c9c49fa87 | ||
|
24a6beb600 | ||
|
0775a45316 | ||
|
90dbdee282 | ||
|
7d28c52c35 | ||
|
7686e012c3 | ||
|
2cb9666678 | ||
|
0ab4e0d1d2 | ||
|
30cc95713f | ||
|
ae49aa6456 | ||
|
89454fcba9 | ||
|
5a6453729c | ||
|
0706ec1a0b | ||
|
405e828683 | ||
|
34a78e635e | ||
|
c327c15825 | ||
|
6d027ae810 | ||
|
104013c916 | ||
|
8df1f04141 | ||
|
beab3599bf | ||
|
819d3ea158 | ||
|
83fd2440ce | ||
|
e13451061f | ||
|
e82c0fea84 | ||
|
e391750e0e | ||
|
6513bb1320 | ||
|
0fb6e25682 | ||
|
7fbfc9df90 | ||
|
cd8237d773 | ||
|
aaa750bbc6 | ||
|
3d54da75cc | ||
|
358584ba6f | ||
|
adfab5f0f3 | ||
|
e37a519e3a | ||
|
6783f0a167 | ||
|
6d78fe749a | ||
|
2120c7967e | ||
|
de861ce781 | ||
|
e222937736 | ||
|
e289811097 | ||
|
067a000b2b | ||
|
f9d05824a7 | ||
|
d753f1eea4 | ||
|
3c81b527eb | ||
|
96147bf6df | ||
|
8b33ce64a4 | ||
|
73fdd4b47e | ||
|
680e404d05 | ||
|
aae6f1a7e9 | ||
|
5cda1ceb0c | ||
|
7ed4efedc3 | ||
|
96ecd620cf |
@@ -8,8 +8,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:versionCode="13101"
|
||||
android:versionName="1.31.1">
|
||||
android:versionCode="13207"
|
||||
android:versionName="1.32.7">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
@@ -69,7 +69,9 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/KdeConnectTheme.NoActionBar"
|
||||
android:name="org.kde.kdeconnect.KdeConnect"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:requestLegacyExternalStorage="true"> <!-- requestLegacyExternalStorage is only used in Android 10: https://developer.android.com/training/data-storage/use-cases#opt-out-in-production-app -->
|
||||
|
||||
|
||||
<receiver
|
||||
android:name="com.android.mms.transaction.PushReceiver"
|
||||
@@ -120,6 +122,9 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
|
||||
|
149
build.gradle.kts
149
build.gradle.kts
@@ -1,6 +1,15 @@
|
||||
import com.android.build.api.instrumentation.AsmClassVisitorFactory
|
||||
import com.android.build.api.instrumentation.ClassContext
|
||||
import com.android.build.api.instrumentation.ClassData
|
||||
import com.android.build.api.instrumentation.InstrumentationParameters
|
||||
import com.android.build.api.instrumentation.InstrumentationScope
|
||||
import com.github.jk1.license.LicenseReportExtension
|
||||
import com.github.jk1.license.render.ReportRenderer
|
||||
import com.github.jk1.license.render.TextReportRenderer
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes.CHECKCAST
|
||||
import org.objectweb.asm.Opcodes.INVOKESTATIC
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
@@ -42,7 +51,7 @@ android {
|
||||
compileSdk = 34
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
targetSdk = 33
|
||||
targetSdk = 35
|
||||
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
|
||||
}
|
||||
buildFeatures {
|
||||
@@ -131,8 +140,139 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix PosixFilePermission class type check issue.
|
||||
*
|
||||
* It fixed the class cast exception when lib desugar enabled and minSdk < 26.
|
||||
*/
|
||||
abstract class FixPosixFilePermissionClassVisitorFactory :
|
||||
AsmClassVisitorFactory<FixPosixFilePermissionClassVisitorFactory.Params> {
|
||||
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor
|
||||
): ClassVisitor {
|
||||
return object : ClassVisitor(instrumentationContext.apiVersion.get(), nextClassVisitor) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
if (name == "attributesToPermissions") { // org.apache.sshd.sftp.common.SftpHelper.attributesToPermissions
|
||||
return object : MethodVisitor(
|
||||
instrumentationContext.apiVersion.get(),
|
||||
super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
) {
|
||||
override fun visitTypeInsn(opcode: Int, type: String?) {
|
||||
// We need to prevent Android Desugar modifying the `PosixFilePermission` classname.
|
||||
//
|
||||
// Android Desugar will replace `CHECKCAST java/nio/file/attribute/PosixFilePermission`
|
||||
// to `CHECKCAST j$/nio/file/attribute/PosixFilePermission`.
|
||||
// We need to replace it with `CHECKCAST java/lang/Enum` to prevent Android Desugar from modifying it.
|
||||
if (opcode == CHECKCAST && type == "java/nio/file/attribute/PosixFilePermission") {
|
||||
println("Bypass PosixFilePermission type check success.")
|
||||
// `Enum` is the superclass of `PosixFilePermission`.
|
||||
// Due to `Object` is not the superclass of `Enum`, we need to use `Enum` instead of `Object`.
|
||||
super.visitTypeInsn(opcode, "java/lang/Enum")
|
||||
} else {
|
||||
super.visitTypeInsn(opcode, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return (classData.className == "org.apache.sshd.sftp.common.SftpHelper").also {
|
||||
if (it) println("SftpHelper Found! Instrumenting...")
|
||||
}
|
||||
}
|
||||
|
||||
interface Params : InstrumentationParameters
|
||||
}
|
||||
|
||||
/**
|
||||
* Collections.unmodifiableXXX is not exist when Android API level is lower than 26.
|
||||
* So we replace the call to Collections.unmodifiableXXX with the original collection by removing the call.
|
||||
*/
|
||||
abstract class FixCollectionsClassVisitorFactory :
|
||||
AsmClassVisitorFactory<FixCollectionsClassVisitorFactory.Params> {
|
||||
override fun createClassVisitor(
|
||||
classContext: ClassContext,
|
||||
nextClassVisitor: ClassVisitor
|
||||
): ClassVisitor {
|
||||
return object : ClassVisitor(instrumentationContext.apiVersion.get(), nextClassVisitor) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
return object : MethodVisitor(
|
||||
instrumentationContext.apiVersion.get(),
|
||||
super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
) {
|
||||
override fun visitMethodInsn(
|
||||
opcode: Int,
|
||||
type: String?,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
isInterface: Boolean
|
||||
) {
|
||||
val backportClass = "org/kde/kdeconnect/Helpers/CollectionsBackport"
|
||||
|
||||
if (opcode == INVOKESTATIC && type == "java/util/Collections") {
|
||||
val replaceRules = mapOf(
|
||||
"unmodifiableNavigableSet" to "(Ljava/util/NavigableSet;)Ljava/util/NavigableSet;",
|
||||
"unmodifiableSet" to "(Ljava/util/Set;)Ljava/util/Set;",
|
||||
"unmodifiableNavigableMap" to "(Ljava/util/NavigableMap;)Ljava/util/NavigableMap;",
|
||||
"emptyNavigableMap" to "()Ljava/util/NavigableMap;")
|
||||
if (name in replaceRules && descriptor == replaceRules[name]) {
|
||||
super.visitMethodInsn(opcode, backportClass, name, descriptor, isInterface)
|
||||
val calleeClass = classContext.currentClassData.className
|
||||
println("Replace Collections.$name call with CollectionsBackport.$name from $calleeClass success.")
|
||||
return
|
||||
}
|
||||
}
|
||||
super.visitMethodInsn(opcode, type, name, descriptor, isInterface)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isInstrumentable(classData: ClassData): Boolean {
|
||||
return classData.className.startsWith("org.apache.sshd") // We only need to fix the Apache SSHD library
|
||||
}
|
||||
|
||||
interface Params : InstrumentationParameters
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
variant.instrumentation.transformClassesWith(
|
||||
FixPosixFilePermissionClassVisitorFactory::class.java,
|
||||
InstrumentationScope.ALL
|
||||
) { }
|
||||
variant.instrumentation.transformClassesWith(
|
||||
FixCollectionsClassVisitorFactory::class.java,
|
||||
InstrumentationScope.ALL
|
||||
) { }
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring(libs.android.desugarJdkLibs)
|
||||
// It has a bug that causes a crash when using PosixFilePermission and minSdk < 26.
|
||||
// It has been used in SSHD Core.
|
||||
// We have taken a workaround to fix it.
|
||||
// See `FixPosixFilePermissionClassVisitorFactory` for more details.
|
||||
coreLibraryDesugaring(libs.android.desugarJdkLibsNio)
|
||||
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
@@ -159,7 +299,10 @@ dependencies {
|
||||
implementation(libs.slf4j.handroid)
|
||||
|
||||
implementation(libs.apache.sshd.core)
|
||||
implementation(libs.apache.mina.core) //For some reason, makes sshd-core:0.14.0 work without NIO, which isn't available until Android 8 (api 26)
|
||||
implementation(libs.apache.sshd.sftp)
|
||||
implementation(libs.apache.sshd.scp)
|
||||
implementation(libs.apache.sshd.mina)
|
||||
implementation(libs.apache.mina.core)
|
||||
|
||||
//implementation("com.github.bright:slf4android:0.1.6") { transitive = true } // For org.apache.sshd debugging
|
||||
implementation(libs.bcpkix.jdk15on) //For SSL certificate generation
|
||||
|
21
fastlane/metadata/android/bg/full_description.txt
Normal file
21
fastlane/metadata/android/bg/full_description.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
KDE Connect предоставя набор от функции за интегриране на вашия работен процес на различни устройства:
|
||||
|
||||
- Прехвърляйте файлове между вашите устройства.
|
||||
- Осъществявайте достъп до файлове на телефона си от компютъра си, без кабели.
|
||||
- Споделен клипборд: копирайте и поставяйте между вашите устройства.
|
||||
- Получавайте известия за входящи обаждания и съобщения на вашия компютър.
|
||||
- Виртуален тъчпад: Използвайте екрана на телефона си като тъчпад на компютъра.
|
||||
- Синхронизиране на известия: Достъп до известията на телефона ви от вашия компютър и отговаряне на съобщения.
|
||||
- Мултимедийно дистанционно управление: Използвайте телефона си като дистанционно за Linux медийни плейъри.
|
||||
- WiFi връзка: не е необходим USB кабел или bluetooth.
|
||||
- TLS криптиране от край до край: информацията ви е в безопасност.
|
||||
|
||||
Моля, имайте предвид, че ще трябва да инсталирате KDE Connect на вашия компютър, за да работи това приложение, и поддържайте версията за настолен компютър актуална с версията за Android, за да работят най-новите функции.
|
||||
|
||||
Поверителна информация за разрешения:
|
||||
* Разрешение за достъпност: Изисква се за получаване на вход от друго устройство за управление на вашия телефон с Android, ако използвате функцията за отдалечено въвеждане.
|
||||
* Разрешение за местоположение във фонов режим: Изисква се, за да знаете към коя WiFi мрежа сте свързани, ако използвате функцията Trusted Networks.
|
||||
|
||||
KDE Connect никога не изпраща никаква информация на KDE или на трета страна. KDE Connect изпраща данни от едно устройство на друго директно чрез локалната мрежа, никога през интернет, и чрез криптиране от край до край.
|
||||
|
||||
Това приложение е част от проект с отворен код и съществува благодарение на всички хора, които са допринесли за него. Посетете уебсайта, за да вземете изходния код.
|
1
fastlane/metadata/android/bg/short_description.txt
Normal file
1
fastlane/metadata/android/bg/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
KDE Connect интегрира вашия смартфон и компютър
|
1
fastlane/metadata/android/bg/title.txt
Normal file
1
fastlane/metadata/android/bg/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
KDE Connect
|
@@ -1,21 +1,21 @@
|
||||
KDE Connect provides a set of features to integrate your workflow across devices:
|
||||
KDE Connect bietet eine Reihe von Funktionen, um Ihre Arbeitsabläufe über verschiedene Geräte zu vereinigen:
|
||||
|
||||
- Transfer files between your devices.
|
||||
- Access files on your phone from your computer, without wires.
|
||||
- Shared clipboard: copy and paste between your devices.
|
||||
- Get notifications for incoming calls and messages on your computer.
|
||||
- Virtual touchpad: Use your phone screen as your computer's touchpad.
|
||||
- Notifications sync: Access your phone notifications from your computer and reply to messages.
|
||||
- Multimedia remote control: Use your phone as a remote for Linux media players.
|
||||
- WiFi connection: no USB wire or bluetooth needed.
|
||||
- End-to-end TLS encryption: your information is safe.
|
||||
- Daten zwischen Ihren Geräten übertragen.
|
||||
- Auf Daten auf Ihrem Telefon von Ihrem Computer aus zugreifen, ohne Kabel.
|
||||
- Geteilte Zwischenablage: Kopieren und Einfügen zwischen Ihren Geräten.
|
||||
- Erhalten Sie Benachrichtigungen über eingehende Anrufe und Nachrichten auf Ihren Computer.
|
||||
- Virtuelles Touchpad: Verwenden Sie den Bildschirm Ihres Telefons als Touchpad für Ihren Computer.
|
||||
- Abgleich der Benachrichtigungen: Greifen Sie über den Computer auf Ihre Telefonbenachrichtigungen zu und antworten Sie auf Nachrichten.
|
||||
- Multimedia-Fernbedienung: Verwenden Sie Ihr Telefon als Fernbedienung für Linux-Medienspieler.
|
||||
- WLAN-Verbindung: kein USB-Kabel oder Bluetooth erforderlich.
|
||||
- Ende-zu-Ende-TLS-Verschlüsselung: Ihre Informationen sind sicher.
|
||||
|
||||
Please note you will need to install KDE Connect on your computer for this app to work, and keep the desktop version up-to-date with the Android version for the latest features to work.
|
||||
Bitte beachten Sie, dass Sie KDE Connect auf Ihrem Computer installieren müssen, damit diese App funktioniert und halten Sie die Desktop-Version mit der Android-Version auf dem aktuellen Stand, um die neuesten Funktionen nutzen zu können.
|
||||
|
||||
Sensitive permissions information:
|
||||
* Accessibility permission: Required to receive input from another device to control your Android phone, if you use the Remote Input feature.
|
||||
* Background location permission: Required to know to which WiFi network you are connected to, if you use the Trusted Networks feature.
|
||||
Informationen zu sensiblen Berechtigungen:
|
||||
* Zugriffsberechtigung: Wird benötigt, um Eingaben zur Steuerung ihres Android-Telefons von einem anderen Gerät zu erhalten, wenn Sie die Ferneingabefunktion verwenden.
|
||||
* Berechtigung den Standort im Hintergrund zu nutzen: Wird benötigt, um festzustellen, mit welchem WLAN-Netzwerk Sie verbunden sind, wenn Sie die Funktion „Vertrauenswürdige Netzwerke” verwenden.
|
||||
|
||||
KDE Connect never sends any information to KDE nor to any third party. KDE Connect sends data from one device to the other directly using the local network, never through the internet, and using end to end encryption.
|
||||
KDE Connect sendet niemals irgendwelche Informationen an KDE oder an Dritte. KDE Connect sendet Daten, unter Verwendung einer Ende-zu-Ende-Verschlüsselung, über das lokale Netzwerk direkt von einem Gerät zum anderen, niemals über das Internet.
|
||||
|
||||
This app is part of an open source project and it exists thanks to all the people who contributed to it. Visit the website to grab the source code.
|
||||
Diese App ist Teil eines Open-Scource-Projekts und besteht Dank all der Menschen die dazu beigetragen haben. Besuchen Sie die Internetseite, um sich den Quelltext zu holen.
|
10
fastlane/metadata/android/en-US/changelogs/13200.txt
Normal file
10
fastlane/metadata/android/en-US/changelogs/13200.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
1.32
|
||||
* Rewrite the remote file browsing
|
||||
* Add Direct Share targets
|
||||
* Send album art from phone to PC
|
||||
|
||||
1.31
|
||||
* Allow sharing URLs to disconnected devices, to be opened when they become available later
|
||||
* Show a notification to continue playing media on this device after stopping it on another device
|
||||
* Display a shortened version of the pairing verification key
|
||||
* Tweaks to the app theme
|
13
fastlane/metadata/android/en-US/changelogs/13201.txt
Normal file
13
fastlane/metadata/android/en-US/changelogs/13201.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
1.32.1
|
||||
* Fixed a crash when opening the presentation remote
|
||||
|
||||
1.32
|
||||
* Rewrite the remote file browsing
|
||||
* Add Direct Share targets
|
||||
* Send album art from phone to PC
|
||||
|
||||
1.31
|
||||
* Allow sharing URLs to disconnected devices, to be opened when they become available later
|
||||
* Show a notification to continue playing media on this device after stopping it on another device
|
||||
* Display a shortened version of the pairing verification key
|
||||
* Tweaks to the app theme
|
11
fastlane/metadata/android/en-US/changelogs/13202.txt
Normal file
11
fastlane/metadata/android/en-US/changelogs/13202.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
1.32.2
|
||||
* Handle expired certificates
|
||||
* Support doubletap drag in remote mouse
|
||||
|
||||
1.32.1
|
||||
* Fixed a crash when opening the presentation remote
|
||||
|
||||
1.32
|
||||
* Rewrite the remote file browsing
|
||||
* Add Direct Share targets
|
||||
* Send album art from phone to PC
|
14
fastlane/metadata/android/en-US/changelogs/13203.txt
Normal file
14
fastlane/metadata/android/en-US/changelogs/13203.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
1.32.3
|
||||
* Fix trusted devices list
|
||||
|
||||
1.32.2
|
||||
* Handle expired certificates
|
||||
* Support doubletap drag in remote mouse
|
||||
|
||||
1.32.1
|
||||
* Fixed a crash when opening the presentation remote
|
||||
|
||||
1.32
|
||||
* Rewrite the remote file browsing
|
||||
* Add Direct Share targets
|
||||
* Send album art from phone to PC
|
17
fastlane/metadata/android/en-US/changelogs/13205.txt
Normal file
17
fastlane/metadata/android/en-US/changelogs/13205.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
1.32.5
|
||||
* Fixed crash in Android 14+
|
||||
|
||||
1.32.3
|
||||
* Fix trusted devices list
|
||||
|
||||
1.32.2
|
||||
* Handle expired certificates
|
||||
* Support doubletap drag in remote mouse
|
||||
|
||||
1.32.1
|
||||
* Fixed a crash when opening the presentation remote
|
||||
|
||||
1.32
|
||||
* Rewrite the remote file browsing
|
||||
* Add Direct Share targets
|
||||
* Send album art from phone to PC
|
16
fastlane/metadata/android/en-US/changelogs/13207.txt
Normal file
16
fastlane/metadata/android/en-US/changelogs/13207.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
1.32.7
|
||||
* Fixed file transfers showing as failed when they succeeded
|
||||
* Fixed plugin list not updating after granting permissions
|
||||
|
||||
1.32.5
|
||||
* Fixed crash in Android 14+
|
||||
|
||||
1.32.3
|
||||
* Fix trusted devices list
|
||||
|
||||
1.32.2
|
||||
* Handle expired certificates
|
||||
* Support doubletap drag in remote mouse
|
||||
|
||||
1.32.1
|
||||
* Fixed a crash when opening the presentation remote
|
@@ -6,7 +6,7 @@ KDEConnect fournit un ensemble de fonctionnalités pour intégrer votre flux de
|
||||
- Apparition de notifications pour les appels et les messages entrants sur votre ordinateur.
|
||||
- Pavé tactile virtuel : utilisation de l'écran de votre téléphone comme pavé tactile pour votre ordinateur.
|
||||
- Synchronisation de vos notifications : accès à vos notifications téléphoniques depuis votre ordinateur et réponses aux messages.
|
||||
- Télé-commande multimédia : utilisation de votre téléphone comme télécommande pour les lecteurs de médias sous Linux.
|
||||
- Télé-commande multimédia : utilisation de votre téléphone comme télécommande pour les lecteurs de média sous Linux.
|
||||
- Connexion au Wifi : aucun connexion USB ou Bluetooth nécessaire.
|
||||
- Chiffrement « TLS » de bout en bout : vos informations sont en sécurité.
|
||||
|
||||
@@ -18,4 +18,4 @@ Informations sur les permissions sensibles :
|
||||
|
||||
KDEConnect n'envoie jamais d'informations à KDE ni à aucun tiers. KDEConnect envoie des données d'un périphérique à un autre à l'aide du réseau local, mais jamais par Internet et en utilisant le chiffrement de bout en bout.
|
||||
|
||||
Cette application fait partie d'un projet « Open source ». Il existe grâce à toutes les personnes qui y ont contribué.Visitez le site Internet pour accéder au code source.
|
||||
Cette application fait partie d'un projet « Open source ». Il existe grâce à toutes les personnes qui y ont contribué. Veuillez visiter le site Internet pour accéder au code source.
|
21
fastlane/metadata/android/no-NO/full_description.txt
Normal file
21
fastlane/metadata/android/no-NO/full_description.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
KDE Connect tilbyr eit sett funksjonar som lèt deg enkelt arbeida på tvers av einingar:
|
||||
|
||||
– Overfør filer mellom einingane
|
||||
– Få trådlaus tilgang til filer på telefonen frå datamaskina
|
||||
– Del utklippsbilete: kopier og lim inn mellom einingane
|
||||
– Vert varsla på datamaskina om innkommande samtalar og tekstmeldingar
|
||||
– Virtuell styreplate: bruk telefonskjermen som styreplate for datamaskina
|
||||
– Synkronisering av varslingar: få tilgang til telefonvarslingar frå datamaskina og svar på meldingar
|
||||
– Fjernkontroll av medieavspeling: bruk telefonen til å styra Linux-baserte mediespelarar
|
||||
– Wi-Fi-tilkopling: du treng ikkje USB- eller Bluetooth-tilkopling
|
||||
– Ende-til-ende-kryptering: informasjonen din er trygg
|
||||
|
||||
Merk at du må installera KDE Connect på datamaskina for å kunna bruka appen. Hugs å halda PC-versjonen oppdatert med Android-versjonen for tilgang til dei nyaste funksjonane.
|
||||
|
||||
Informasjon om sensitive løyve:
|
||||
– Tilgjenge-løyve: Trengst for å kunna ta imot tastetrykk frå PC for å styra Android-eininga om du brukar funksjonen «Fjernstyring»
|
||||
– Bakgrunnsløyve til å sjå geografiske posisjon: Trengst for å veta kva Wi-Fi-nettverk du er tilkopla om du brukar funksjonen «Tiltrudde nettverk»
|
||||
|
||||
KDE Connect sender aldri informasjon til KDE eller nokon tredjepart. Programmet sender data direkte mellom dei to einingane via lokalnettet, aldri via Internett og alltid med ende-til-ende-kryptering.
|
||||
|
||||
Appen er ein del av eit fri programvare-prosjekt og er blitt til takka vera mange bidragsytarar. Gå til heimesida for å sjå kjeldekoden.
|
1
fastlane/metadata/android/no-NO/short_description.txt
Normal file
1
fastlane/metadata/android/no-NO/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
KDE Connect koplar telefonen din saman med datamaskina
|
1
fastlane/metadata/android/no-NO/title.txt
Normal file
1
fastlane/metadata/android/no-NO/title.txt
Normal file
@@ -0,0 +1 @@
|
||||
KDE Connect
|
@@ -1,20 +1,21 @@
|
||||
O KDE Connect fornece um conjunto de recursos para integrar seu fluxo de trabalho entre dispositivos:
|
||||
|
||||
- Transfira arquivos entre seus dispositivos.
|
||||
- Acesse arquivos do seu computador no seu telefone, sem fios.
|
||||
- Área de transferência compartilhada: copie e cole entre seus dispositivos.
|
||||
- Compartilhe arquivos e URLs em seu computador a partir de qualquer app.
|
||||
- Receba notificações de chamadas recebidas e mensagens SMS no seu PC.
|
||||
- Touchpad virtual: use a tela do telefone como touchpad do computador.
|
||||
- Sincronização de notificações: leia as notificações do seu Android na área de trabalho.
|
||||
- Controle remoto multimídia: use seu telefone como controle remoto para reprodutores de mídia Linux.
|
||||
- Sincronização de notificações: acesse as notificações do seu telefone no seu computador e responda as mensagens.
|
||||
- Controle remoto multimídia: use seu telefone como controle remoto para reprodutores de mídia no Linux.
|
||||
- Conexão Wi-Fi: sem necessidade de cabos USB ou bluetooth.
|
||||
- Criptografia TLS de ponta a ponta: suas informações estão seguras.
|
||||
|
||||
Observe que você precisará instalar o KDE Connect no seu computador para que este aplicativo funcione e mantenha a versão para desktop atualizada com a versão do Android para que os recursos mais recentes funcionem.
|
||||
|
||||
Informações a respeito de permissões especiais :
|
||||
Informações sobre permissões sensíveis:
|
||||
* Permissão de acessibilidade: necessária para receber entrada de outro dispositivo para controlar seu telefone Android, se você usar o recurso de entrada remota.
|
||||
* Permissão de localização em segundo plano: necessária para saber a qual rede Wi-Fi você está conectado, se você usar o recurso de redes confiáveis.
|
||||
|
||||
O KDE Connect nunca envia nenhuma informação ao KDE nem a terceiros. O KDE Connect envia dados de um dispositivo para outro diretamente usando a rede local, nunca pela Internet e usando criptografia de ponta a ponta.
|
||||
O KDE Connect nunca envia nenhuma informação ao KDE ou a terceiros. O KDE Connect envia dados de um dispositivo para outro diretamente usando a rede local, nunca pela Internet e usando criptografia de ponta a ponta.
|
||||
|
||||
Este aplicativo faz parte de um projeto de código aberto e existe graças a todas as pessoas que contribuíram para ele. Visite o site para obter o código-fonte.
|
@@ -1,44 +1,44 @@
|
||||
[versions]
|
||||
activityCompose = "1.9.0"
|
||||
androidDesugarJdkLibs = "2.0.4"
|
||||
androidGradlePlugin = "8.4.1"
|
||||
activityCompose = "1.9.2"
|
||||
androidDesugarJdkLibs = "2.1.2"
|
||||
androidGradlePlugin = "8.6.1"
|
||||
androidSmsmms = "kdeconnect-1-21-0"
|
||||
appcompat = "1.7.0"
|
||||
bcpkixJdk15on = "1.70"
|
||||
classindex = "3.13"
|
||||
commonsCollections4 = "4.4"
|
||||
commonsIo = "2.16.1"
|
||||
commonsLang3 = "3.14.0"
|
||||
commonsIo = "2.17.0"
|
||||
commonsLang3 = "3.17.0"
|
||||
constraintlayoutCompose = "1.0.1"
|
||||
coreKtx = "1.13.1"
|
||||
dependencyLicenseReport = "2.7"
|
||||
disklrucache = "2.0.2"
|
||||
documentfile = "1.0.1"
|
||||
gridlayout = "1.0.0"
|
||||
jsonassert = "1.5.1"
|
||||
jsonassert = "1.5.3"
|
||||
junit = "4.13.2"
|
||||
kotlin = "2.0.0"
|
||||
kotlinxCoroutinesCore = "1.8.1"
|
||||
kotlin = "2.0.20"
|
||||
kotlinxCoroutinesCore = "1.9.0"
|
||||
lifecycleExtensions = "2.2.0"
|
||||
lifecycleRuntimeKtx = "2.8.1"
|
||||
lifecycleRuntimeKtx = "2.8.6"
|
||||
logger = "1.0.3"
|
||||
material = "1.12.0"
|
||||
material3 = "1.2.1"
|
||||
material3 = "1.3.0"
|
||||
media = "1.7.0"
|
||||
minaCore = "2.0.19"
|
||||
mockitoCore = "5.12.0"
|
||||
minaCore = "2.2.3"
|
||||
mockitoCore = "5.14.0"
|
||||
preferenceKtx = "1.2.1"
|
||||
reactiveStreams = "1.0.4"
|
||||
recyclerview = "1.3.2"
|
||||
rxjava = "2.2.21"
|
||||
sl4j = "2.0.4"
|
||||
sshdCore = "0.14.0"
|
||||
sl4j = "2.0.13"
|
||||
sshdCore = "2.13.2"
|
||||
swiperefreshlayout = "1.1.0"
|
||||
uiToolingPreview = "1.6.7"
|
||||
uiToolingPreview = "1.7.2"
|
||||
univocityParsers = "2.9.1"
|
||||
|
||||
[libraries]
|
||||
android-desugarJdkLibs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" }
|
||||
android-desugarJdkLibsNio = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "androidDesugarJdkLibs" }
|
||||
android-smsmms = { module = "org.kde.invent.sredman:android-smsmms", version.ref = "androidSmsmms" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" }
|
||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||
@@ -74,6 +74,9 @@ logger = { module = "com.klinkerapps:logger", version.ref = "logger" }
|
||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||
apache-mina-core = { module = "org.apache.mina:mina-core", version.ref = "minaCore" }
|
||||
apache-sshd-core = { module = "org.apache.sshd:sshd-core", version.ref = "sshdCore" }
|
||||
apache-sshd-sftp = { module = "org.apache.sshd:sshd-sftp", version.ref = "sshdCore" }
|
||||
apache-sshd-scp = { module = "org.apache.sshd:sshd-scp", version.ref = "sshdCore" }
|
||||
apache-sshd-mina = { module = "org.apache.sshd:sshd-mina", version.ref = "sshdCore" }
|
||||
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" }
|
||||
reactive-streams = { module = "org.reactivestreams:reactive-streams", version.ref = "reactiveStreams" }
|
||||
rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" }
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,8 @@
|
||||
#Sat Mar 02 00:26:28 CET 2024
|
||||
#Sat Sep 28 01:39:16 AM EDT 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
294
gradlew
vendored
294
gradlew
vendored
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,78 +17,111 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -97,87 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
55
gradlew.bat
vendored
55
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -54,48 +55,36 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
85
po/bg/kdeconnect-android-store-full.po
Normal file
85
po/bg/kdeconnect-android-store-full.po
Normal file
@@ -0,0 +1,85 @@
|
||||
# SPDX-FileCopyrightText: 2024 Mincho Kondarev <mkondarev@yahoo.de>
|
||||
#. extracted from ./metadata/android/en-US/full_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-05 12:31+0000\n"
|
||||
"PO-Revision-Date: 2024-07-28 18:31+0200\n"
|
||||
"Last-Translator: Mincho Kondarev <mkondarev@yahoo.de>\n"
|
||||
"Language-Team: Bulgarian <kde-i18n-doc@kde.org>\n"
|
||||
"Language: bg\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Lokalize 24.07.70\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
|
||||
msgid ""
|
||||
"KDE Connect provides a set of features to integrate your workflow across "
|
||||
"devices:\n"
|
||||
"\n"
|
||||
"- Transfer files between your devices.\n"
|
||||
"- Access files on your phone from your computer, without wires.\n"
|
||||
"- Shared clipboard: copy and paste between your devices.\n"
|
||||
"- Get notifications for incoming calls and messages on your computer.\n"
|
||||
"- Virtual touchpad: Use your phone screen as your computer's touchpad.\n"
|
||||
"- Notifications sync: Access your phone notifications from your computer and "
|
||||
"reply to messages.\n"
|
||||
"- Multimedia remote control: Use your phone as a remote for Linux media "
|
||||
"players.\n"
|
||||
"- WiFi connection: no USB wire or bluetooth needed.\n"
|
||||
"- End-to-end TLS encryption: your information is safe.\n"
|
||||
"\n"
|
||||
"Please note you will need to install KDE Connect on your computer for this "
|
||||
"app to work, and keep the desktop version up-to-date with the Android "
|
||||
"version for the latest features to work.\n"
|
||||
"\n"
|
||||
"Sensitive permissions information:\n"
|
||||
"* Accessibility permission: Required to receive input from another device to "
|
||||
"control your Android phone, if you use the Remote Input feature.\n"
|
||||
"* Background location permission: Required to know to which WiFi network you "
|
||||
"are connected to, if you use the Trusted Networks feature.\n"
|
||||
"\n"
|
||||
"KDE Connect never sends any information to KDE nor to any third party. KDE "
|
||||
"Connect sends data from one device to the other directly using the local "
|
||||
"network, never through the internet, and using end to end encryption.\n"
|
||||
"\n"
|
||||
"This app is part of an open source project and it exists thanks to all the "
|
||||
"people who contributed to it. Visit the website to grab the source code.\n"
|
||||
msgstr ""
|
||||
"KDE Connect предоставя набор от функции за интегриране на вашия работен "
|
||||
"процес на различни устройства:\n"
|
||||
"\n"
|
||||
"- Прехвърляйте файлове между вашите устройства.\n"
|
||||
"- Осъществявайте достъп до файлове на телефона си от компютъра си, без "
|
||||
"кабели.\n"
|
||||
"- Споделен клипборд: копирайте и поставяйте между вашите устройства.\n"
|
||||
"- Получавайте известия за входящи обаждания и съобщения на вашия компютър.\n"
|
||||
"- Виртуален тъчпад: Използвайте екрана на телефона си като тъчпад на "
|
||||
"компютъра.\n"
|
||||
"- Синхронизиране на известия: Достъп до известията на телефона ви от вашия "
|
||||
"компютър и отговаряне на съобщения.\n"
|
||||
"- Мултимедийно дистанционно управление: Използвайте телефона си като "
|
||||
"дистанционно за Linux медийни плейъри.\n"
|
||||
"- WiFi връзка: не е необходим USB кабел или bluetooth.\n"
|
||||
"- TLS криптиране от край до край: информацията ви е в безопасност.\n"
|
||||
"\n"
|
||||
"Моля, имайте предвид, че ще трябва да инсталирате KDE Connect на вашия "
|
||||
"компютър, за да работи това приложение, и поддържайте версията за настолен "
|
||||
"компютър актуална с версията за Android, за да работят най-новите функции.\n"
|
||||
"\n"
|
||||
"Поверителна информация за разрешения:\n"
|
||||
"* Разрешение за достъпност: Изисква се за получаване на вход от друго "
|
||||
"устройство за управление на вашия телефон с Android, ако използвате "
|
||||
"функцията за отдалечено въвеждане.\n"
|
||||
"* Разрешение за местоположение във фонов режим: Изисква се, за да знаете към "
|
||||
"коя WiFi мрежа сте свързани, ако използвате функцията Trusted Networks.\n"
|
||||
"\n"
|
||||
"KDE Connect никога не изпраща никаква информация на KDE или на трета страна. "
|
||||
"KDE Connect изпраща данни от едно устройство на друго директно чрез "
|
||||
"локалната мрежа, никога през интернет, и чрез криптиране от край до край.\n"
|
||||
"\n"
|
||||
"Това приложение е част от проект с отворен код и съществува благодарение на "
|
||||
"всички хора, които са допринесли за него. Посетете уебсайта, за да вземете "
|
||||
"изходния код.\n"
|
19
po/bg/kdeconnect-android-store-short.po
Normal file
19
po/bg/kdeconnect-android-store-short.po
Normal file
@@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2024 Mincho Kondarev <mkondarev@yahoo.de>
|
||||
#. extracted from ./metadata/android/en-US/short_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-05 12:31+0000\n"
|
||||
"PO-Revision-Date: 2024-07-28 18:32+0200\n"
|
||||
"Last-Translator: Mincho Kondarev <mkondarev@yahoo.de>\n"
|
||||
"Language-Team: Bulgarian <kde-i18n-doc@kde.org>\n"
|
||||
"Language: bg\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Lokalize 24.07.70\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
|
||||
msgid "KDE Connect integrates your smartphone and computer"
|
||||
msgstr "KDE Connect интегрира вашия смартфон и компютър"
|
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2024 Vit Pelcak <vpelcak@suse.cz>
|
||||
# SPDX-FileCopyrightText: 2024 Vit Pelcak <vit@pelcak.org>
|
||||
#. extracted from ./metadata/android/en-US/short_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
|
@@ -1,19 +1,20 @@
|
||||
# Frederik Schwarzer <schwarzer@kde.org>, 2023.
|
||||
# tobi <onewayme001@posteo.de>, 2024.
|
||||
#. extracted from ./metadata/android/en-US/full_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Project-Id-Version: kdeconnect-android-store-full\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-05 12:31+0000\n"
|
||||
"PO-Revision-Date: 2023-06-07 19:50+0200\n"
|
||||
"Last-Translator: Frederik Schwarzer <schwarzer@kde.org>\n"
|
||||
"PO-Revision-Date: 2024-10-02 20:37+0200\n"
|
||||
"Last-Translator: tobi <onewayme001@posteo.de>\n"
|
||||
"Language-Team: German <kde-i18n-de@kde.org>\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Lokalize 23.07.70\n"
|
||||
"X-Generator: Lokalize 21.12.3\n"
|
||||
|
||||
msgid ""
|
||||
"KDE Connect provides a set of features to integrate your workflow across "
|
||||
@@ -48,3 +49,41 @@ msgid ""
|
||||
"This app is part of an open source project and it exists thanks to all the "
|
||||
"people who contributed to it. Visit the website to grab the source code.\n"
|
||||
msgstr ""
|
||||
"KDE Connect bietet eine Reihe von Funktionen, um Ihre Arbeitsabläufe über "
|
||||
"verschiedene Geräte zu vereinigen:\n"
|
||||
"\n"
|
||||
"- Daten zwischen Ihren Geräten übertragen.\n"
|
||||
"- Auf Daten auf Ihrem Telefon von Ihrem Computer aus zugreifen, ohne Kabel.\n"
|
||||
"- Geteilte Zwischenablage: Kopieren und Einfügen zwischen Ihren Geräten.\n"
|
||||
"- Erhalten Sie Benachrichtigungen über eingehende Anrufe und Nachrichten auf "
|
||||
"Ihren Computer.\n"
|
||||
"- Virtuelles Touchpad: Verwenden Sie den Bildschirm Ihres Telefons als "
|
||||
"Touchpad für Ihren Computer.\n"
|
||||
"- Abgleich der Benachrichtigungen: Greifen Sie über den Computer auf Ihre "
|
||||
"Telefonbenachrichtigungen zu und antworten Sie auf Nachrichten.\n"
|
||||
"- Multimedia-Fernbedienung: Verwenden Sie Ihr Telefon als Fernbedienung für "
|
||||
"Linux-Medienspieler.\n"
|
||||
"- WLAN-Verbindung: kein USB-Kabel oder Bluetooth erforderlich.\n"
|
||||
"- Ende-zu-Ende-TLS-Verschlüsselung: Ihre Informationen sind sicher.\n"
|
||||
"\n"
|
||||
"Bitte beachten Sie, dass Sie KDE Connect auf Ihrem Computer installieren "
|
||||
"müssen, damit diese App funktioniert und halten Sie die Desktop-Version mit "
|
||||
"der Android-Version auf dem aktuellen Stand, um die neuesten Funktionen "
|
||||
"nutzen zu können.\n"
|
||||
"\n"
|
||||
"Informationen zu sensiblen Berechtigungen:\n"
|
||||
"* Zugriffsberechtigung: Wird benötigt, um Eingaben zur Steuerung ihres "
|
||||
"Android-Telefons von einem anderen Gerät zu erhalten, wenn Sie die "
|
||||
"Ferneingabefunktion verwenden. \n"
|
||||
"* Berechtigung den Standort im Hintergrund zu nutzen: Wird benötigt, um "
|
||||
"festzustellen, mit welchem WLAN-Netzwerk Sie verbunden sind, wenn Sie die "
|
||||
"Funktion „Vertrauenswürdige Netzwerke” verwenden.\n"
|
||||
"\n"
|
||||
"KDE Connect sendet niemals irgendwelche Informationen an KDE oder an Dritte. "
|
||||
"KDE Connect sendet Daten, unter Verwendung einer Ende-zu-Ende-"
|
||||
"Verschlüsselung, über das lokale Netzwerk direkt von einem Gerät zum "
|
||||
"anderen, niemals über das Internet.\n"
|
||||
"\n"
|
||||
"Diese App ist Teil eines Open-Scource-Projekts und besteht Dank all der "
|
||||
"Menschen die dazu beigetragen haben. Besuchen Sie die Internetseite, um sich "
|
||||
"den Quelltext zu holen.\n"
|
||||
|
@@ -2,7 +2,7 @@
|
||||
#. extracted from ./metadata/android/en-US/short_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Project-Id-Version: kdeconnect-android-store-short\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-05 12:31+0000\n"
|
||||
"PO-Revision-Date: 2023-06-07 19:50+0200\n"
|
||||
|
@@ -1,20 +1,20 @@
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2023 Xavier Besnard <xavier.besnard@kde.org>
|
||||
# SPDX-FileCopyrightText: 2023, 2024 Xavier Besnard <xavier.besnard@kde.org>
|
||||
#. extracted from ./metadata/android/en-US/full_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: kdeconnect-android-store-full\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-05 12:31+0000\n"
|
||||
"PO-Revision-Date: 2023-09-28 18:06+0200\n"
|
||||
"Last-Translator: Xavier BESNARD <xavier.besnard@neuf.fr>\n"
|
||||
"Language-Team: French <kde-francophone@kde.org>\n"
|
||||
"PO-Revision-Date: 2024-08-09 22:07+0200\n"
|
||||
"Last-Translator: Xavier Besnard <xavier.besnard@kde.org>\n"
|
||||
"Language-Team: French <French <kde-francophone@kde.org>>\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: Lokalize 23.08.1\n"
|
||||
"X-Generator: Lokalize 23.08.5\n"
|
||||
|
||||
msgid ""
|
||||
"KDE Connect provides a set of features to integrate your workflow across "
|
||||
@@ -62,7 +62,7 @@ msgstr ""
|
||||
"- Synchronisation de vos notifications : accès à vos notifications "
|
||||
"téléphoniques depuis votre ordinateur et réponses aux messages.\n"
|
||||
"- Télé-commande multimédia : utilisation de votre téléphone comme "
|
||||
"télécommande pour les lecteurs de médias sous Linux.\n"
|
||||
"télécommande pour les lecteurs de média sous Linux.\n"
|
||||
"- Connexion au Wifi : aucun connexion USB ou Bluetooth nécessaire.\n"
|
||||
"- Chiffrement « TLS » de bout en bout : vos informations sont en sécurité.\n"
|
||||
"\n"
|
||||
@@ -83,5 +83,5 @@ msgstr ""
|
||||
"mais jamais par Internet et en utilisant le chiffrement de bout en bout.\n"
|
||||
"\n"
|
||||
"Cette application fait partie d'un projet « Open source ». Il existe grâce à "
|
||||
"toutes les personnes qui y ont contribué.Visitez le site Internet pour "
|
||||
"accéder au code source.\n"
|
||||
"toutes les personnes qui y ont contribué. Veuillez visiter le site Internet "
|
||||
"pour accéder au code source.\n"
|
||||
|
85
po/nn/kdeconnect-android-store-full.po
Normal file
85
po/nn/kdeconnect-android-store-full.po
Normal file
@@ -0,0 +1,85 @@
|
||||
# Translation of kdeconnect-android-store-full to Norwegian Nynorsk
|
||||
#
|
||||
#. extracted from ./metadata/android/en-US/full_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-05 12:31+0000\n"
|
||||
"PO-Revision-Date: 2024-07-10 20:18+0200\n"
|
||||
"Last-Translator: Karl Ove Hufthammer <karl@huftis.org>\n"
|
||||
"Language-Team: Norwegian Nynorsk <l10n-no@lister.huftis.org>\n"
|
||||
"Language: nn\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Lokalize 24.05.1\n"
|
||||
"X-Environment: kde\n"
|
||||
"X-Accelerator-Marker: &\n"
|
||||
"X-Text-Markup: kde4\n"
|
||||
|
||||
msgid ""
|
||||
"KDE Connect provides a set of features to integrate your workflow across "
|
||||
"devices:\n"
|
||||
"\n"
|
||||
"- Transfer files between your devices.\n"
|
||||
"- Access files on your phone from your computer, without wires.\n"
|
||||
"- Shared clipboard: copy and paste between your devices.\n"
|
||||
"- Get notifications for incoming calls and messages on your computer.\n"
|
||||
"- Virtual touchpad: Use your phone screen as your computer's touchpad.\n"
|
||||
"- Notifications sync: Access your phone notifications from your computer and "
|
||||
"reply to messages.\n"
|
||||
"- Multimedia remote control: Use your phone as a remote for Linux media "
|
||||
"players.\n"
|
||||
"- WiFi connection: no USB wire or bluetooth needed.\n"
|
||||
"- End-to-end TLS encryption: your information is safe.\n"
|
||||
"\n"
|
||||
"Please note you will need to install KDE Connect on your computer for this "
|
||||
"app to work, and keep the desktop version up-to-date with the Android "
|
||||
"version for the latest features to work.\n"
|
||||
"\n"
|
||||
"Sensitive permissions information:\n"
|
||||
"* Accessibility permission: Required to receive input from another device to "
|
||||
"control your Android phone, if you use the Remote Input feature.\n"
|
||||
"* Background location permission: Required to know to which WiFi network you "
|
||||
"are connected to, if you use the Trusted Networks feature.\n"
|
||||
"\n"
|
||||
"KDE Connect never sends any information to KDE nor to any third party. KDE "
|
||||
"Connect sends data from one device to the other directly using the local "
|
||||
"network, never through the internet, and using end to end encryption.\n"
|
||||
"\n"
|
||||
"This app is part of an open source project and it exists thanks to all the "
|
||||
"people who contributed to it. Visit the website to grab the source code.\n"
|
||||
msgstr ""
|
||||
"KDE Connect tilbyr eit sett funksjonar som lèt deg enkelt arbeida på tvers "
|
||||
"av einingar:\n"
|
||||
"\n"
|
||||
"– Overfør filer mellom einingane\n"
|
||||
"– Få trådlaus tilgang til filer på telefonen frå datamaskina\n"
|
||||
"– Del utklippsbilete: kopier og lim inn mellom einingane\n"
|
||||
"– Vert varsla på datamaskina om innkommande samtalar og tekstmeldingar\n"
|
||||
"– Virtuell styreplate: bruk telefonskjermen som styreplate for datamaskina\n"
|
||||
"– Synkronisering av varslingar: få tilgang til telefonvarslingar frå "
|
||||
"datamaskina og svar på meldingar\n"
|
||||
"– Fjernkontroll av medieavspeling: bruk telefonen til å styra Linux-baserte "
|
||||
"mediespelarar\n"
|
||||
"– Wi-Fi-tilkopling: du treng ikkje USB- eller Bluetooth-tilkopling\n"
|
||||
"– Ende-til-ende-kryptering: informasjonen din er trygg\n"
|
||||
"\n"
|
||||
"Merk at du må installera KDE Connect på datamaskina for å kunna bruka appen. "
|
||||
"Hugs å halda PC-versjonen oppdatert med Android-versjonen for tilgang til "
|
||||
"dei nyaste funksjonane.\n"
|
||||
"\n"
|
||||
"Informasjon om sensitive løyve:\n"
|
||||
"– Tilgjenge-løyve: Trengst for å kunna ta imot tastetrykk frå PC for å styra "
|
||||
"Android-eininga om du brukar funksjonen «Fjernstyring»\n"
|
||||
"– Bakgrunnsløyve til å sjå geografiske posisjon: Trengst for å veta kva Wi-"
|
||||
"Fi-nettverk du er tilkopla om du brukar funksjonen «Tiltrudde nettverk»\n"
|
||||
"\n"
|
||||
"KDE Connect sender aldri informasjon til KDE eller nokon tredjepart. "
|
||||
"Programmet sender data direkte mellom dei to einingane via lokalnettet, "
|
||||
"aldri via Internett og alltid med ende-til-ende-kryptering.\n"
|
||||
"\n"
|
||||
"Appen er ein del av eit fri programvare-prosjekt og er blitt til takka vera "
|
||||
"mange bidragsytarar. Gå til heimesida for å sjå kjeldekoden.\n"
|
@@ -1,53 +1,21 @@
|
||||
# Geraldo Simiao <geraldosimiao@fedoraproject.org>, 2023.
|
||||
# Frederico Goncalves Guimaraes <frederico@teia.bio.br>, 2024.
|
||||
#. extracted from ./metadata/android/en-US/full_description.txt
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-11-05 12:31+0000\n"
|
||||
"PO-Revision-Date: 2023-08-04 01:33-0300\n"
|
||||
"Last-Translator: Geraldo Simiao <geraldosimiao@fedoraproject.org>\n"
|
||||
"PO-Revision-Date: 2024-08-28 17:37-0300\n"
|
||||
"Last-Translator: Frederico Goncalves Guimaraes <frederico@teia.bio.br>\n"
|
||||
"Language-Team: Brazilian Portuguese <kde-i18n-pt_BR@kde.org>\n"
|
||||
"Language: pt_BR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Lokalize 23.04.3\n"
|
||||
"X-Generator: Lokalize 22.12.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "KDE Connect provides a set of features to integrate your workflow across "
|
||||
#| "devices:\n"
|
||||
#| "\n"
|
||||
#| "- Shared clipboard: copy and paste between your devices.\n"
|
||||
#| "- Share files and URLs to your computer from any app.\n"
|
||||
#| "- Get notifications for incoming calls and SMS messages on your PC.\n"
|
||||
#| "- Virtual touchpad: Use your phone screen as your computer's touchpad.\n"
|
||||
#| "- Notifications sync: Read your Android notifications from the desktop.\n"
|
||||
#| "- Multimedia remote control: Use your phone as a remote for Linux media "
|
||||
#| "players.\n"
|
||||
#| "- WiFi connection: no USB wire or bluetooth needed.\n"
|
||||
#| "- End-to-end TLS encryption: your information is safe.\n"
|
||||
#| "\n"
|
||||
#| "Please note you will need to install KDE Connect on your computer for "
|
||||
#| "this app to work, and keep the desktop version up-to-date with the "
|
||||
#| "Android version for the latest features to work.\n"
|
||||
#| "\n"
|
||||
#| "Sensitive permissions information:\n"
|
||||
#| "* Accessibility permission: Required to receive input from another device "
|
||||
#| "to control your Android phone, if you use the Remote Input feature.\n"
|
||||
#| "* Background location permission: Required to know to which WiFi network "
|
||||
#| "you are connected to, if you use the Trusted Networks feature.\n"
|
||||
#| "\n"
|
||||
#| "KDE Connect never sends any information to KDE nor to any third party. "
|
||||
#| "KDE Connect sends data from one device to the other directly using the "
|
||||
#| "local network, never through the internet, and using end to end "
|
||||
#| "encryption.\n"
|
||||
#| "\n"
|
||||
#| "This app is part of an open source project and it exists thanks to all "
|
||||
#| "the people who contributed to it. Visit the website to grab the source "
|
||||
#| "code.\n"
|
||||
msgid ""
|
||||
"KDE Connect provides a set of features to integrate your workflow across "
|
||||
"devices:\n"
|
||||
@@ -84,15 +52,16 @@ msgstr ""
|
||||
"O KDE Connect fornece um conjunto de recursos para integrar seu fluxo de "
|
||||
"trabalho entre dispositivos:\n"
|
||||
"\n"
|
||||
"- Transfira arquivos entre seus dispositivos.\n"
|
||||
"- Acesse arquivos do seu computador no seu telefone, sem fios.\n"
|
||||
"- Área de transferência compartilhada: copie e cole entre seus "
|
||||
"dispositivos.\n"
|
||||
"- Compartilhe arquivos e URLs em seu computador a partir de qualquer app.\n"
|
||||
"- Receba notificações de chamadas recebidas e mensagens SMS no seu PC.\n"
|
||||
"- Touchpad virtual: use a tela do telefone como touchpad do computador.\n"
|
||||
"- Sincronização de notificações: leia as notificações do seu Android na área "
|
||||
"de trabalho.\n"
|
||||
"- Sincronização de notificações: acesse as notificações do seu telefone no "
|
||||
"seu computador e responda as mensagens.\n"
|
||||
"- Controle remoto multimídia: use seu telefone como controle remoto para "
|
||||
"reprodutores de mídia Linux.\n"
|
||||
"reprodutores de mídia no Linux.\n"
|
||||
"- Conexão Wi-Fi: sem necessidade de cabos USB ou bluetooth.\n"
|
||||
"- Criptografia TLS de ponta a ponta: suas informações estão seguras.\n"
|
||||
"\n"
|
||||
@@ -100,14 +69,14 @@ msgstr ""
|
||||
"este aplicativo funcione e mantenha a versão para desktop atualizada com a "
|
||||
"versão do Android para que os recursos mais recentes funcionem.\n"
|
||||
"\n"
|
||||
"Informações a respeito de permissões especiais :\n"
|
||||
"Informações sobre permissões sensíveis:\n"
|
||||
"* Permissão de acessibilidade: necessária para receber entrada de outro "
|
||||
"dispositivo para controlar seu telefone Android, se você usar o recurso de "
|
||||
"entrada remota.\n"
|
||||
"* Permissão de localização em segundo plano: necessária para saber a qual "
|
||||
"rede Wi-Fi você está conectado, se você usar o recurso de redes confiáveis.\n"
|
||||
"\n"
|
||||
"O KDE Connect nunca envia nenhuma informação ao KDE nem a terceiros. O KDE "
|
||||
"O KDE Connect nunca envia nenhuma informação ao KDE ou a terceiros. O KDE "
|
||||
"Connect envia dados de um dispositivo para outro diretamente usando a rede "
|
||||
"local, nunca pela Internet e usando criptografia de ponta a ponta.\n"
|
||||
"\n"
|
||||
|
13
res/drawable/ic_device_desktop_shortcut.xml
Normal file
13
res/drawable/ic_device_desktop_shortcut.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group android:pivotX="12" android:pivotY="12" android:scaleX="0.66" android:scaleY="0.66">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,2L3,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h7v2L8,20v2h8v-2h-2v-2h7c1.1,0 2,-0.9 2,-2L23,4c0,-1.1 -0.9,-2 -2,-2zM21,16L3,16L3,4h18v12z" />
|
||||
</group>
|
||||
</vector>
|
17
res/drawable/ic_device_laptop_shortcut.xml
Normal file
17
res/drawable/ic_device_laptop_shortcut.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group
|
||||
android:pivotX="12"
|
||||
android:pivotY="12"
|
||||
android:scaleX="0.66"
|
||||
android:scaleY="0.66">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,18c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2H4C2.9,4 2,4.9 2,6v10c0,1.1 0.9,2 2,2H0v2h24v-2H20zM4,6h16v10H4V6z" />
|
||||
</group>
|
||||
</vector>
|
18
res/drawable/ic_device_phone_shortcut.xml
Normal file
18
res/drawable/ic_device_phone_shortcut.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group
|
||||
android:pivotX="12"
|
||||
android:pivotY="12"
|
||||
android:scaleX="0.66"
|
||||
android:scaleY="0.66">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M16,1L8,1C6.34,1 5,2.34 5,4v16c0,1.66 1.34,3 3,3h8c1.66,0 3,-1.34 3,-3L19,4c0,-1.66 -1.34,-3 -3,-3zM14,21h-4v-1h4v1zM17.25,18L6.75,18L6.75,4h10.5v14z" />
|
||||
|
||||
</group>
|
||||
</vector>
|
18
res/drawable/ic_device_tablet_shortcut.xml
Normal file
18
res/drawable/ic_device_tablet_shortcut.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group
|
||||
android:pivotX="12"
|
||||
android:pivotY="12"
|
||||
android:scaleX="0.66"
|
||||
android:scaleY="0.66">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,4L3,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 1.99,-0.9 1.99,-2L23,6c0,-1.1 -0.9,-2 -2,-2zM19,18L5,18L5,6h14v12z" />
|
||||
|
||||
</group>
|
||||
</vector>
|
17
res/drawable/ic_device_tv_shortcut.xml
Normal file
17
res/drawable/ic_device_tv_shortcut.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group
|
||||
android:pivotX="12"
|
||||
android:pivotY="12"
|
||||
android:scaleX="0.66"
|
||||
android:scaleY="0.66">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,17L3,17L3,5h18v12z" />
|
||||
</group>
|
||||
</vector>
|
@@ -84,9 +84,11 @@
|
||||
<string name="error_canceled_by_other_peer">ألغاه ندّ آخر</string>
|
||||
<string name="encryption_info_title">معلومات التّعمية</string>
|
||||
<string name="pair_requested">طُلب الاقتران</string>
|
||||
<string name="pair_succeeded">نجح الاقتران</string>
|
||||
<string name="tap_to_open">اطرق لتفتح</string>
|
||||
<string name="received_file_text">المس لفتح \'%1s\'</string>
|
||||
<string name="tap_to_answer">المس للإجابة</string>
|
||||
<string name="left_click">أرسل نقرة باليسار</string>
|
||||
<string name="right_click">أرسل نقرة باليمين</string>
|
||||
<string name="middle_click">أرسل نقرة بالوسط</string>
|
||||
<string name="show_keyboard">أظهر لوحة المفاتيح</string>
|
||||
@@ -113,6 +115,7 @@
|
||||
<item>دقيقة واحدة</item>
|
||||
<item>دقيقتان</item>
|
||||
</string-array>
|
||||
<string name="share_to">شارك مع…</string>
|
||||
<string name="protocol_version_newer">يستخدم هذا الجهاز إصدار ميفاق أحدث</string>
|
||||
<string name="plugin_settings_with_name">إعدادات %s</string>
|
||||
<string name="invalid_device_name">اسم جهاز غير صالح</string>
|
||||
@@ -145,8 +148,10 @@
|
||||
<string name="no_file_browser">لا متصفّحات ملفّات مثبّتة.</string>
|
||||
<string name="pref_plugin_telepathy">أرسل SMS</string>
|
||||
<string name="pref_plugin_telepathy_desc">أرسل رسائل نصّيّة من سطح المكتب</string>
|
||||
<string name="pref_plugin_telepathy_mms">أرسل رسالة قصيرة</string>
|
||||
<string name="findmyphone_title">جِد جهازي</string>
|
||||
<string name="findmyphone_title_tablet">جِد جهازي اللوحيّ</string>
|
||||
<string name="findmyphone_title_tv">جِد تلفازي</string>
|
||||
<string name="findmyphone_description">يرّن هذا الجهاز لتجده</string>
|
||||
<string name="findmyphone_found">عثر عليه</string>
|
||||
<string name="open">افتح</string>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Изпращане</string>
|
||||
<string name="compose_send_title">Текстът е изпратен</string>
|
||||
<string name="open_compose_send">Съставяне на текст</string>
|
||||
<string name="double_tap_to_drag">Двойно докосване за влачене</string>
|
||||
<string name="hold_to_drag">Задържане за влачене</string>
|
||||
<string name="about_kde_about"><h1>За</h1> <p>KDE е световна общност от софтуерни инженери, художници, писатели, преводачи и творци, които са отдадени на <a href=https://www.gnu.org/philosophy/free-sw.html>свободното разработване на софтуер</a>. KDE създава работната среда Plasma, стотици приложения и многобройните софтуерни библиотеки, които ги поддържат.</p> <p>KDE е кооперативно предприятие: нито една отделна организация контролира насоките или продуктите му. Вместо това ние работим заедно, за да постигнем общата цел да създадем най-добрия свободен софтуер в света. Всеки е добре дошъл да се присъедини и да да допринесе</a> за KDE, включително и вие.</p> Посетете <a href=https://www.kde.org/>https://www.kde.org/</a> за повече информация за общността на KDE и за софтуера, който създаваме.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes">" <h1>Докладвайте за грешки или желания</h1> <p>Софтуерът винаги може да бъде подобрен и екипът на KDE е готов да го направи. Въпреки това вие - потребителят - трябва да да ни кажете, когато нещо не работи според очакванията или може да бъде направено по-добре.</p> <p>KDE разполага със система за проследяване на грешки. Посетете <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> или използвайте бутона \"Докладване на грешка\" от екрана за програмата, за да съобщите за грешки.</p> Ако имате предложение за подобрение, тогава можете да използвате системата за проследяване на грешки, за да регистрирате желанието си. Уверете се, че използвате тежестта, наречена \"Wishlist\"."</string>
|
||||
<string name="about_kde_join_kde"><h1>Присъединете се към KDE</h1> <p>Не е необходимо да сте софтуерен разработчик на софтуер, за да сте член на екипа на KDE. Можете да се присъедините към преводаческите екипи за вашия език. Можете да предоставяте графики, теми, звуци, и подобрена документация. Вие решавате!</p> <p>Посетете <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> за информация относно някои проекти, в които можете да да участвате.</p> Ако имате нужда от повече информация или документация, тогава посетете <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> ще ви предостави необходимото.</string>
|
||||
@@ -403,6 +405,7 @@
|
||||
<string name="maxim_leshchenko_task">Подобрения на потребителския интерфейс и тази страница за</string>
|
||||
<string name="holger_kaelberer_task">Плъгин за отдалечена клавиатура и поправки на грешки</string>
|
||||
<string name="saikrishna_arcot_task">Поддръжка за използване на клавиатура в плъгина за отдалечено въвеждане, поправки на грешки и общи подобрения</string>
|
||||
<string name="shellwen_chen_task">Внедряване на SFTP, подобряване на възможностите за поддръжка на този проект, поправки на грешки и общи подобрения</string>
|
||||
<string name="everyone_else">Всички останали, които са допринесли за KDE Connect през годините</string>
|
||||
<string name="send_clipboard">Изпращане на клипборд</string>
|
||||
<string name="tap_to_execute">Докоснете, за да се изпълни</string>
|
||||
|
@@ -366,7 +366,7 @@
|
||||
<item>Clar</item>
|
||||
<item>Fosc</item>
|
||||
</string-array>
|
||||
<string name="report_bug">Informa d\'un error</string>
|
||||
<string name="report_bug">Informeu d\'un error</string>
|
||||
<string name="donate">Donació de diners</string>
|
||||
<string name="source_code">Codi font</string>
|
||||
<string name="licenses">Llicències</string>
|
||||
@@ -389,8 +389,10 @@
|
||||
<string name="send_compose">Envia</string>
|
||||
<string name="compose_send_title">Títol de l\'enviament</string>
|
||||
<string name="open_compose_send">Redacta text</string>
|
||||
<string name="double_tap_to_drag">Dos tocs per a arrossegar</string>
|
||||
<string name="hold_to_drag">Mantenir premut per a arrossegar</string>
|
||||
<string name="about_kde_about"><h1>Quant al</h1> <p>El KDE és una comunitat mundial d\'enginyers, artistes, escriptors, traductors i creadors de programari compromesos amb el desenvolupament de <a href=https://www.gnu.org/philosophy/free-sw.html>programari lliure</a>. El KDE produeix l\'entorn d\'escriptori Plasma, centenars d\'aplicacions i moltes biblioteques de programari que els donen suport.</p> <p>El KDE és una empresa en cooperativa: cap entitat controla la seva direcció o els productes. En el seu lloc, treballem junts per a aconseguir l\'objectiu comú de construir el millor programari lliure del món. Tothom hi és benvingut a <a href=https://community.kde.org/Get_Involved>unir-se i contribuir</a> al KDE, inclosos vosaltres.</p> Visiteu <a href=https://www.kde.org/ca/>https://www.kde.org/ca/</a> per a obtenir més informació sobre la comunitat KDE i el programari que produïm.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Informeu dels errors o desitjos</h1> <p>El programari sempre es pot millorar, i l\'equip del KDE està a punt per a fer-ho. No obstant això, l\'usuari, ha de dir-nos quan alguna cosa no funciona com s\'esperava o si podria fer-se millor.</p> <p>El KDE té un sistema de seguiment d\'errors. Per a informar-ne d\'un, visiteu <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> o useu el botó \"Informa d\'un error\" des de la pantalla Quant al.</p> Si teniu un suggeriment de millora, podeu usar el sistema de seguiment d\'errors per a enregistrar el vostre desig. Assegureu-vos d\'usar la severitat anomenada \"Llista de desitjos\" (Wishlist).</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Informeu dels errors o desitjos</h1> <p>El programari sempre es pot millorar, i l\'equip del KDE està a punt per a fer-ho. No obstant això, l\'usuari, ha de dir-nos quan alguna cosa no funciona com s\'esperava o si podria fer-se millor.</p> <p>El KDE té un sistema de seguiment d\'errors. Per a informar-ne d\'un, visiteu <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> o useu el botó «Informeu d\'un error» des de la pantalla Quant al.</p> Si teniu un suggeriment de millora, podeu usar el sistema de seguiment d\'errors per a enregistrar el vostre desig. Assegureu-vos d\'usar la severitat anomenada «Llista de desitjos» (Wishlist).</string>
|
||||
<string name="about_kde_join_kde"><h1>Uniu-vos al KDE</h1> <p>No cal ser un desenvolupador de programari per a ser membre de l\'equip KDE. Podeu unir-vos als equips d\'idiomes que tradueixen la interfície dels programes. Podeu proporcionar gràfics, temes, sons i documentació millorada. Vosaltres decidiu!</p> <p>Visiteu <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> per a obtenir informació sobre alguns projectes en què podeu participar-hi.</p> Si us cal més informació o documentació, una visita a <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> us proporcionarà el que necessiteu.</string>
|
||||
<string name="about_kde_support_kde"><h1>Contribució al KDE</h1> <p>El programari KDE està i sempre estarà disponible de forma gratuïta, però la creació no està lliure de càrrecs.</p> <p>Per a donar suport al desenvolupament, la comunitat KDE ha format la KDE e.V., una organització sense ànim de lucre legalment fundada a Alemanya. La KDE e.V. representa a la comunitat KDE en els assumptes legals i financers. Per a obtenir informació sobre la KDE e.V., vegeu <a href=https://ev.kde.org/>https://ev.kde.org/</a>.El KDE es beneficia de molts tipus de contribucions, inclosa la financera. Usem els fons per a reemborsar als membres i altra gent per les despeses que incorren col·laborant-hi. S\'usen més fons per al suport legal i l\'organització de les conferències i reunions.</p> <p>Us animem a ajudar al KDE mitjançant donacions monetàries, usant un dels mitjans descrits a <a href=https://kde.org/ca/community/donations/>https://kde.org/ca/community/donations/</a>.</p>. Moltes gràcies per endavant per la vostra ajuda.</string>
|
||||
<string name="maintainer_and_developer">Mantenidor i desenvolupador</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Millores en la IU i ha creat aquesta pàgina</string>
|
||||
<string name="holger_kaelberer_task">Connector de teclat remot i esmenes d\'errors</string>
|
||||
<string name="saikrishna_arcot_task">Suport per a usar el teclat en el connector d\'entrada remota, esmenes d\'errors i millores generals</string>
|
||||
<string name="shellwen_chen_task">Millorar la seguretat d\'SFTP, millorar la mantenibilitat d\'aquest projecte, esmenes d\'errors i millores generals</string>
|
||||
<string name="everyone_else">Tothom qui ha contribuït al KDE Connect al llarg dels anys</string>
|
||||
<string name="send_clipboard">Envia el porta-retalls</string>
|
||||
<string name="tap_to_execute">Toqueu per a executar</string>
|
||||
<string name="plugin_stats">Estadístiques del connector</string>
|
||||
<string name="enable_udp_broadcast">Activa el descobriment UDP de dispositius</string>
|
||||
<string name="enable_bluetooth">Activa el Bluetooth (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Cal permetre que les notificacions es rebin des d\'altres dispositius</string>
|
||||
<string name="findmyphone_notifications_explanation">Cal el permís de notificacions perquè el telèfon pugui sonar quan l\'aplicació estigui en segon pla</string>
|
||||
<string name="no_notifications">Les notificacions estan desactivades, no rebreu notificacions entrants d\'emparellament.</string>
|
||||
|
@@ -207,6 +207,7 @@
|
||||
<string name="mpris_notification_settings_summary">Umožnit ovládání přehrávače médií bez otevření KDE Connect</string>
|
||||
<string name="share_to">Sdílet s...</string>
|
||||
<string name="unreachable_device">%s (nedostupná)</string>
|
||||
<string name="unreachable_device_url_share_text">URL sdílená s nedostupným zařízením budou zaslána jakmile se stane dostupným.\n\n</string>
|
||||
<string name="protocol_version_newer">Toto zařízení používá novější verzi protokolu</string>
|
||||
<string name="plugin_settings_with_name">Nastavení %s</string>
|
||||
<string name="invalid_device_name">Neplatný název zařízení</string>
|
||||
@@ -400,6 +401,8 @@
|
||||
<string name="clear_compose">Vyprázdnit</string>
|
||||
<string name="send_compose">Odeslat</string>
|
||||
<string name="open_compose_send">Napsat text</string>
|
||||
<string name="double_tap_to_drag">Přetáhnout dvojitým ťuknutím</string>
|
||||
<string name="hold_to_drag">Přetáhnout podržením</string>
|
||||
<string name="about_kde_about"><h1>O KDE</h1> <p>KDE je celosvětová komunita softwarových inženýrů, výtvarníků, překladatelů a jiných přispěvatelů, kteří se odevzdali vývoji <a href=https://www.gnu.org/philosophy/free-sw.html>Svobodného Softwaru</a>. KDE vytvořilo pracovní prostředí Plasma, stovky aplikací a spousty knihoven, jenž je podporují. </p> <p>KDE je společné úsilí, kde žádná společnost neřídí jeho směr nebo produkty. Namísto toho spolupracujeme na společném cíli jímž je vytvoření nejlepšího Free Softwaru. Každý je vítán aby <a href=https://community.kde.org/Get_Involved>se zapojil a přispíval</a> do KDE, včetně vás. Více informací o komunitě KDE a softwaru, na kterém pracujeme najdete na </a>$3<a href=https://www.kde.org/>https://www.kde.org/</a>.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Hlaste chyby a návrhy</h1> <p>Software je možno neustále vylepšovat a tým KDE je k tomu připraven. Avšak vy, uživatel, nám musíte sdělit, když něco nefunguje tak, jak by se očekávalo nebo by mělo být uděláno lépe.</p> <p>KDE má systém sledování chyb. Chcete-li tedy nahlásit chybu, navštivte <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> nebo použijte dialog \"Nahlásit chybu...\".</p> Máte-li náměty na vylepšení, budeme rádi, pošlete-li nám svoje přání. Ujistěte se však, že jste označili chybové hlášení jako \"Přání\".</string>
|
||||
<string name="about_kde_join_kde"><h1>Přidejte se ke KDE</h1> <p>K tomu, abyste se stali členem týmu KDE, není zapotřebí být vývojářem softwaru. Můžete se připojit k překladatelským týmům, které překládají programy. Můžete vytvářet grafiku, motivy, zvuky a lepší dokumentaci. Vy se rozhodněte!</p> <p>Navštivte <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> kde naleznete informace o některých projektech, kterých se můžete zúčastnit.</p> Potřebujete-li více informací nebo dokumentace, pak návštěva na <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> vám poskytne, co potřebujete.</p>České stránky o KDE se nacházejí na adrese <a href=\"http://czechia.kde.org/\">http://czechia.kde.org/</a> . Přidejte se k nám!</string>
|
||||
@@ -414,6 +417,7 @@
|
||||
<string name="maxim_leshchenko_task">Vylepšení prostředí a tato stránka o aplikaci</string>
|
||||
<string name="holger_kaelberer_task">Vzdálené modul klávesnice a opravy chyb</string>
|
||||
<string name="saikrishna_arcot_task">Podpora použití klávesnice na vzdáleném vstupním modulu, opravy chyb a obecná zlepšení</string>
|
||||
<string name="shellwen_chen_task">Vylepšení zabezpečení SFTP, zlepšení udržovatelnosti projektu, opravy chyb a obecná vylepšení</string>
|
||||
<string name="everyone_else">Každý kdo přispěl do KDE Connect během let</string>
|
||||
<string name="send_clipboard">Poslat schránku</string>
|
||||
<string name="tap_to_execute">Pro spuštění ťukněte sem</string>
|
||||
@@ -424,5 +428,6 @@
|
||||
<string name="no_notifications">Upozornění jsou zakázána. Neuvidíte upozornění na párování.</string>
|
||||
<string name="mpris_keepwatching">Pokračovat v přehrávání</string>
|
||||
<string name="mpris_keepwatching_settings_title">Pokračovat v přehrávání</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Zobrazit tiché upozornění pro pokračování přehrávání na tomto zařízení po zavření médií</string>
|
||||
<string name="notification_channel_keepwatching">Pokračovat v přehrávání</string>
|
||||
</resources>
|
||||
|
@@ -18,6 +18,7 @@
|
||||
<string name="pref_plugin_clipboard_sent">Zwischenablage versendet</string>
|
||||
<string name="pref_plugin_mousepad">Ferneingabe</string>
|
||||
<string name="pref_plugin_mousepad_desc">Das Gerät als Touchpad und/oder Tastatur verwenden</string>
|
||||
<string name="pref_plugin_presenter">Präsentationsfernbedienung</string>
|
||||
<string name="pref_plugin_presenter_desc">Das Gerät zum Wechseln der Folien einer Präsentation verwenden</string>
|
||||
<string name="pref_plugin_remotekeyboard">Empfänger für Tastatureingaben</string>
|
||||
<string name="pref_plugin_remotekeyboard_desc">Tastatureingaben entfernter Geräte empfangen</string>
|
||||
@@ -51,6 +52,7 @@
|
||||
<string name="remotekeyboard_multiple_connections">Es besteht mehr als eine Verbindungen zu einer entfernten Tastatur. Um Ihre Konfiguration anzupassen, wählen Sie bitte ein Gerät aus</string>
|
||||
<string name="open_mousepad">Ferneingabe</string>
|
||||
<string name="mousepad_info">Bewegen Sie Ihren Finger über den Bildschirm um den Mauszeiger zu bewegen. Tippen Sie auf den Bildschirm, um einen Klick zu simulieren und benutzen Sie entsprechend zwei/drei Finger für einen Rechts-/Mittelklick. Verwenden Sie zwei Finger, um zu Scrollen und einen langen Druck um Objekte zu verschieben. Gyroskop-Maus-Funktionen können Sie in den Modul-Einstellungen aktivieren.</string>
|
||||
<string name="mousepad_info_no_gestures">Bewegen Sie den Finger auf dem Bildschirm, um den Mauszeiger zu bewegen. Tippen Sie, um zu klicken.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Das verbundene Gerät unterstützt keine Tastatureingaben</string>
|
||||
<string name="mousepad_single_tap_settings_title">Aktionsausführung bei Berührung mit einem Finger einstellen</string>
|
||||
<string name="mousepad_double_tap_settings_title">Aktionsausführung bei Berührung mit zwei Fingern einstellen</string>
|
||||
@@ -59,6 +61,7 @@
|
||||
<string name="mousepad_mouse_buttons_title">Maustasten anzeigen</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Zeigerbeschleunigung einstellen</string>
|
||||
<string name="mousepad_scroll_direction_title">Bildlaufrichtung umkehren</string>
|
||||
<string name="mousepad_scroll_sensitivity_title">Bildlaufgeschwindigkeit</string>
|
||||
<string name="gyro_mouse_enabled_title">Gyroskop-Maus aktivieren</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Empfindlichkeit des Gyroskops einstellen</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
@@ -77,9 +80,9 @@
|
||||
<string-array name="mousepad_acceleration_profile_entries">
|
||||
<item>Keine Beschleunigung</item>
|
||||
<item>Schwächste</item>
|
||||
<item>Schwach</item>
|
||||
<item>Schwächer</item>
|
||||
<item>Normal</item>
|
||||
<item>Stark</item>
|
||||
<item>Stärker</item>
|
||||
<item>Stärkste</item>
|
||||
</string-array>
|
||||
<string name="sendkeystrokes_send_to">Tastendruck senden an</string>
|
||||
@@ -115,6 +118,8 @@
|
||||
<string name="my_device_fingerprint">Der SHA256-Fingerabdruck Ihres Gerätezertifikats lautet:</string>
|
||||
<string name="remote_device_fingerprint">Der SHA256-Fingerabdruck des Gerätezertifikats der Gegenstelle lautet:</string>
|
||||
<string name="pair_requested">Verbindung angefordert</string>
|
||||
<string name="pair_succeeded">Kopplung erfolgreich</string>
|
||||
<string name="pairing_request_from">Kopplungsanfrage von „%1s“</string>
|
||||
<plurals name="incoming_file_title">
|
||||
<item quantity="one">%1$d Datei von %2$s wird empfangen</item>
|
||||
<item quantity="other">%1$d Dateien von %2$s werden empfangen</item>
|
||||
@@ -151,6 +156,7 @@
|
||||
<string name="received_file_text">Tippen um „%1s“ zu öffnen</string>
|
||||
<string name="cannot_create_file">Die Datei %s kann nicht erstellt werden</string>
|
||||
<string name="tap_to_answer">Zum Antworten tippen</string>
|
||||
<string name="left_click">Linksklick senden</string>
|
||||
<string name="right_click">Rechtsklick senden</string>
|
||||
<string name="middle_click">Mittelklick senden</string>
|
||||
<string name="show_keyboard">Tastatur anzeigen</string>
|
||||
@@ -177,8 +183,12 @@
|
||||
<item>1 Minute</item>
|
||||
<item>2 Minuten</item>
|
||||
</string-array>
|
||||
<string name="mpris_notifications_explanation">Benachrichtigungen müssen erlaubt sein, um entfernte Medien im Benachrichtigungsfenster zu zeigen.</string>
|
||||
<string name="mpris_notification_settings_title">Benachrichtigung zur Medienkontrolle anzeigen</string>
|
||||
<string name="mpris_notification_settings_summary">Die Steuerung der Medienwiedergabe auch dann erlauben wenn KDE Connect nicht geöffnet ist</string>
|
||||
<string name="share_to">Freigeben für ...</string>
|
||||
<string name="unreachable_device">%s (nicht erreichbar)</string>
|
||||
<string name="unreachable_device_url_share_text">URLs, die mit einem nicht erreichbaren Gerät geteilt werden, werden zugestellt sobald es erreichbar wird.\n\n</string>
|
||||
<string name="protocol_version_newer">Dieses Gerät verwendet eine neuere Protokollversion</string>
|
||||
<string name="plugin_settings_with_name">%s-Einstellungen</string>
|
||||
<string name="invalid_device_name">Ungültiger Gerätename</string>
|
||||
@@ -214,6 +224,7 @@
|
||||
<string name="sftp_action_mode_menu_delete">Löschen</string>
|
||||
<string name="sftp_no_storage_locations_configured">Keine Speicherorte ausgewählt</string>
|
||||
<string name="sftp_saf_permission_explanation">Um von außerhalb auf Ihre Dateien zugreifen zu können, muss mindestens ein Speicherort vorhanden sein</string>
|
||||
<string name="sftp_manage_storage_permission_explanation">Um den Fernzugriff auf Dateien auf diesem Gerät zu erlauben, müssen Sie KDE Connect erlauben den Speicher zu verwalten.</string>
|
||||
<string name="no_players_connected">Keine Medienspieler gefunden</string>
|
||||
<string name="send_files">Dateien senden</string>
|
||||
<string name="block_notification_contents">Benachrichtigungsinhalte blockieren</string>
|
||||
@@ -243,10 +254,13 @@
|
||||
<string name="all_permissions_granted">Alle Berechtigungen erteilt 🎉</string>
|
||||
<string name="optional_permission_explanation">Es müssen weitere Berechtigungen erteilt werden, um alle Funktionen nutzen zu können</string>
|
||||
<string name="plugins_need_optional_permission">Einige Module haben eingeschränkte Funktionen wegen fehlender Berechtigungen, tippen Sie für weitere Informationen:</string>
|
||||
<string name="share_optional_permission_explanation">Um Dateien zu empfangen, müssen Sie Speicherzugriff erlauben</string>
|
||||
<string name="share_notifications_explanation">Um den Fortschritt beim Senden oder Emfangen von Dateien zu sehen, müssen Sie Benachrichtigungen erlauben.</string>
|
||||
<string name="telepathy_permission_explanation">Um SMS vom Rechner aus zu lesen und zu versenden, muss der Zugriff auf die SMS-Funktion gewährt werden</string>
|
||||
<string name="telephony_permission_explanation">Um eingehende Anrufe auf der Arbeitsfläche anzuzeigen, muss der Zugriff auf die Anrufliste und den Telefonstatus gewährt werden</string>
|
||||
<string name="telephony_optional_permission_explanation">Um einen Namen anstelle der Telefonnummer anzuzeigen, muss der Zugriff auf das Adressbuch gewährt werden</string>
|
||||
<string name="contacts_permission_explanation">Um Ihre Kontakte mit der Arbeitsfläche zu teilen, muss der Zugriff auf die Kontakte gewährt werden</string>
|
||||
<string name="contacts_per_device_confirmation">Ihre Telefonkontakte werden auf dieses Gerät übertragen, damit sie von der KDE Connect SMS App und anderen Apps verwendet werden können.</string>
|
||||
<string name="select_ringtone">Einen Klingelton auswählen</string>
|
||||
<string name="telephony_pref_blocked_title">Unterdrückte Nummern</string>
|
||||
<string name="telephony_pref_blocked_dialog_desc">Keine Anrufe und SMS von diesen Telefonnummern anzeigen (Bitte geben Sie eine Nummer pro Zeile ein)</string>
|
||||
@@ -264,6 +278,9 @@
|
||||
<string name="notification_channel_default">Andere Benachrichtigungen</string>
|
||||
<string name="notification_channel_persistent">Dauerhafte Benachrichtigung</string>
|
||||
<string name="notification_channel_media_control">Medienkontrolle</string>
|
||||
<string name="notification_channel_filetransfer">eingehende Dateiübertragung</string>
|
||||
<string name="notification_channel_filetransfer_upload">ausgehende Dateiübertragung</string>
|
||||
<string name="notification_channel_filetransfer_error">Datenübertragungsfehler</string>
|
||||
<string name="notification_channel_high_priority">Hohe Priorität</string>
|
||||
<string name="mpris_stop">Die aktuelle Medienwiedergabe beenden</string>
|
||||
<string name="copy_url_to_clipboard">Adresse in die Zwischenablage kopieren</string>
|
||||
@@ -272,6 +289,7 @@
|
||||
<string name="runcommand_notpaired">Das Gerät ist nicht verbunden</string>
|
||||
<string name="runcommand_nosuchdevice">Ein solches Gerät existiert nicht</string>
|
||||
<string name="runcommand_noruncommandplugin">Dieses Gerät hat das Modul zum Ausführen von Befehlen nicht aktiviert</string>
|
||||
<string name="runcommand_name_as_title_title">Den Namen als Titel anzeigen</string>
|
||||
<string name="pref_plugin_findremotedevice">Entferntes Gerät finden</string>
|
||||
<string name="pref_plugin_findremotedevice_desc">Entferntes Gerät anklingeln</string>
|
||||
<string name="ring">Klingeln</string>
|
||||
@@ -302,6 +320,7 @@
|
||||
<string name="empty_trusted_networks_list_text">Sie haben bisher noch kein vertrauenswürdiges Netzwerk hinzugefügt</string>
|
||||
<string name="allow_all_networks_text">Alle erlauben</string>
|
||||
<string name="location_permission_needed_title">Berechtigung erforderlich</string>
|
||||
<string name="location_permission_needed_desc">KDE connect benötigt die Berechtigung für die Verwendung des Standorts im Hintergrund, um festzustellen, mit welchem WLAN-Netzwerk Sie verbunden sind, auch wenn die App im Hintergrund ist. Dies ist notwendig, da die Name der WLAN-Netzwerke verwendet werden können, um Ihren Standort zu bestimmen, auch wenn KDE Connect dies nicht tut.</string>
|
||||
<string name="clipboard_android_x_incompat">In Android 10 wurde der Zugriff auf die Zwischenablage für alle Apps entfernt. Diese Modul wird deaktiviert.</string>
|
||||
<string name="mpris_open_url">Wiedergabe hier fortsetzen</string>
|
||||
<string name="cant_open_url">Die URL zum Fortsetzen der Wiedergabe kann nicht geöffnet werden</string>
|
||||
@@ -312,6 +331,8 @@
|
||||
<string name="bigscreen_right">Rechts</string>
|
||||
<string name="bigscreen_down">Unten</string>
|
||||
<string name="bigscreen_mic">Mikrofon</string>
|
||||
<string name="pref_plugin_bigscreen">Fernbedienung für Großbildschirm</string>
|
||||
<string name="pref_plugin_bigscreen_desc">Verwenden Sie ihr Gerät als Fernbedienung für einen Plasma-Großbildschirm</string>
|
||||
<string name="bigscreen_optional_permission_explanation">Um den Mikrofoneingang des Mobiltelefons freizugeben, müssen Sie den Zugriff auf den Audioeingang des Mobiltelefons erlauben</string>
|
||||
<string name="bigscreen_speech_extra_prompt">Sprache</string>
|
||||
<string name="message_reply_label">Antworten</string>
|
||||
@@ -347,6 +368,7 @@
|
||||
<string name="about">Über</string>
|
||||
<string name="authors">Autoren</string>
|
||||
<string name="thanks_to">Dank an</string>
|
||||
<string name="easter_egg">Osterei</string>
|
||||
<string name="email_contributor">E-Mail an den Mitwirkenden senden\n%s</string>
|
||||
<string name="visit_contributors_homepage">Internetseite des Mitwirkenden besuchen\n%s</string>
|
||||
<string name="version">Version %s</string>
|
||||
@@ -357,11 +379,23 @@
|
||||
<string name="clear_compose">Leeren</string>
|
||||
<string name="send_compose">Senden</string>
|
||||
<string name="open_compose_send">Text schreiben</string>
|
||||
<string name="double_tap_to_drag">Doppelklicken um zu ziehen</string>
|
||||
<string name="hold_to_drag">Halten um zu ziehen</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Fehler und Wünsche melden</h1> <p>Software kann immer verbessert werden und das KDE-Team ist immer bereit das zu tun. Jedoch müssen Sie - der Benutzer - uns sagen, wenn etwas nicht wie erwartet funktioniert oder verbessert werden könnte.</p> <p>KDE hat ein Bug-Tracking-System. Besuchen Sie <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> oder verwenden Sie den Knopf „Probleme und Wünsche berichten“ auf dem Über-Bildschirm, um Bugs zu melden.</p> Wenn Sie Vorschläge für Verbesserungen haben, können Sie gerne das Bug-Tracking-System nutzen, um Ihren Wunsch zu melden. Verwenden Sie hierzu den Schweregrad „Wunschliste“.</string>
|
||||
<string name="maintainer_and_developer">Betreuer und Entwickler</string>
|
||||
<string name="developer">Entwickler</string>
|
||||
<string name="apple_support">macOS-Unterstützung. Arbeit an der macOS-Unterstützung</string>
|
||||
<string name="bug_fixes_and_general_improvements">Fehlerbereinigung und allgemeine Verbesserungen</string>
|
||||
<string name="aniket_kumar_task">Verbesserungen am SMS-Modul</string>
|
||||
<string name="alex_fiestas_task">Verbesserungen am Kontakte-Modul</string>
|
||||
<string name="maxim_leshchenko_task">UI-Verbesserungen und diese Infoseite</string>
|
||||
<string name="everyone_else">Jeder, der über die Jahre noch zu KDE Connect beigetragen hat</string>
|
||||
<string name="send_clipboard">Zwischenablage senden</string>
|
||||
<string name="tap_to_execute">Tippen um auszuführen</string>
|
||||
<string name="enable_udp_broadcast">UDP-Geräteerkennung einschalten</string>
|
||||
<string name="findmyphone_notifications_explanation">Benachrichtigungen müssen erlaubt sein, damit das Telefon klingeln kann, wenn die App im Hintergrund ist</string>
|
||||
<string name="no_notifications">Benachrichtigungen sind abgeschaltet, Sie erhalten keine Benachrichtigungen für eingehende Verbindungen.</string>
|
||||
<string name="mpris_keepwatching">Wiedergabe fortsetzen</string>
|
||||
<string name="mpris_keepwatching_settings_title">Wiedergabe fortsetzen</string>
|
||||
<string name="notification_channel_keepwatching">Wiedergabe fortsetzen</string>
|
||||
</resources>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Send</string>
|
||||
<string name="compose_send_title">Compose send</string>
|
||||
<string name="open_compose_send">Compose text</string>
|
||||
<string name="double_tap_to_drag">Double tap to drag</string>
|
||||
<string name="hold_to_drag">Hold to drag</string>
|
||||
<string name="about_kde_about"><h1>About</h1> <p>KDE is a world-wide community of software engineers, artists, writers, translators and creators who are committed to <a href=https://www.gnu.org/philosophy/free-sw.html>Free Software</a> development. KDE produces the Plasma desktop environment, hundreds of applications, and the many software libraries that support them.</p> <p>KDE is a cooperative enterprise: no single entity controls its direction or products. Instead, we work together to achieve the common goal of building the world\'s finest Free Software. Everyone is welcome to <a href=https://community.kde.org/Get_Involved>join and contribute</a> to KDE, including you.</p> Visit <a href=https://www.kde.org/>https://www.kde.org/</a> for more information about the KDE community and the software we produce.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Report Bugs or Wishes</h1> <p>Software can always be improved, and the KDE team is ready to do so. However, you - the user - must tell us when something does not work as expected or could be done better.</p> <p>KDE has a bug tracking system. Visit <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> or use the \"Report Bug\" button from the about screen to report bugs.</p> If you have a suggestion for improvement then you are welcome to use the bug tracking system to register your wish. Make sure you use the severity called \"Wishlist\".</string>
|
||||
<string name="about_kde_join_kde"><h1>Join KDE</h1> <p>You do not have to be a software developer to be a member of the KDE team. You can join the language teams that translate program interfaces. You can provide graphics, themes, sounds, and improved documentation. You decide!</p> <p>Visit <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> for information on some projects in which you can participate.</p> If you need more information or documentation, then a visit to <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> will provide you with what you need.</string>
|
||||
@@ -403,6 +405,7 @@
|
||||
<string name="maxim_leshchenko_task">UI improvements and this about page</string>
|
||||
<string name="holger_kaelberer_task">Remote keyboard plugin and bug fixes</string>
|
||||
<string name="saikrishna_arcot_task">Support for using the keyboard in the remote input plugin, bug fixes and general improvements</string>
|
||||
<string name="shellwen_chen_task">Improve the security of SFTP, improve the maintainability of this project, bug fixes and general improvements</string>
|
||||
<string name="everyone_else">Everyone else who has contributed to KDE Connect over the years</string>
|
||||
<string name="send_clipboard">Send clipboard</string>
|
||||
<string name="tap_to_execute">Tap to execute</string>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Enviar</string>
|
||||
<string name="compose_send_title">Componer envío</string>
|
||||
<string name="open_compose_send">Componer texto</string>
|
||||
<string name="double_tap_to_drag">Doble pulsación para arrastrar</string>
|
||||
<string name="hold_to_drag">Mantener para arrastrar</string>
|
||||
<string name="about_kde_about"><h1>Acerca de</h1> <p>KDE es una comunidad global de ingenieros software, artistas, escritores, traductores y creadores que siguen el desarrollo de <a href=https://www.gnu.org/philosophy/free-sw.html>Software Libre</a>. KDE produce el entorno de escritorio Plasma, cientos de aplicaciones y todas las librerías en las que se basan.</p> <p>KDE es una empresa colaborativa: no hay una entidad única que controla sus productos o su dirección. En su lugar, trabajamos de manera conjunta para conseguir la meta común de construir el mejor software libre posible. Todo el mundo es bienvenido a <a href=https://community.kde.org/Get_Involved>unirse y contribuir</a> a KDE, incluido usted.</p> Visite <a href=https://www.kde.org/>https://www.kde.org/</a> para más información sobre la comunidad KDE y el software que creamos.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Reporte errores o deseos</h1> <p>El software siempre puede ser mejorado y el equipo de KDE está preparado para ello. Sin embargo, usted - el usuario - debe comunicarnos cuando algo no funciona como es esperado o que puede ser mejorado. </p> <p> KDE tiene un sistema de traqueo de errores. Visite <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> o use el botón «Informar de fallo» en la ventana «Acerca de» para reportar errores.</p> Si tiene una sugerencia de mejora entonces use el sistema de traqueo de errores para registrar su sugerencia. Asegúrese de que usa la severidad «Lista de deseos».</string>
|
||||
<string name="about_kde_join_kde"><h1>Unirse a KDE</h1> <p>No tiene por que ser un desarrollador de software para ser miembro del equipo KDE. Se puede unir a los equipos de traducción que traducen las interfaces de los programas. Puede proporcionar gráficos, temas, sonidos y mejorar la documentación. ¡Tú decides!</p> <p>Visite <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> para más información sobre los proyectos en los que puede participar.</p> Si necesita más información o documentación, entonces una visita a <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> le proporcionará la información que necesite.</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Mejoras en la UI y la página «Acerca de»</string>
|
||||
<string name="holger_kaelberer_task">Complemento del teclado remoto y arreglos</string>
|
||||
<string name="saikrishna_arcot_task">Soporte para usar el teclado en el complemento de entrada remota, arreglos y mejoras generales</string>
|
||||
<string name="shellwen_chen_task">Mejoras en la seguridad de SFTP, mejoras en la mantenibilidad del proyecto, arreglos de fallos y mejoras generales.</string>
|
||||
<string name="everyone_else">Todos los demás que han contribuido a KDE Connect a lo largo de su historia</string>
|
||||
<string name="send_clipboard">Enviar al portapapeles</string>
|
||||
<string name="tap_to_execute">Pulse para ejecutar</string>
|
||||
<string name="plugin_stats">Estadísticas del complemento</string>
|
||||
<string name="enable_udp_broadcast">Activar descubrimiento de dispositivos por UDP</string>
|
||||
<string name="enable_bluetooth">Activar Bluetooth (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Las notificaciones tienen que estar activadas para recibirlas desde otros dispositivos</string>
|
||||
<string name="findmyphone_notifications_explanation">Se necesita el permiso de notificaciones para que el teléfono pueda sonar cuando la aplicación está en segundo plano</string>
|
||||
<string name="no_notifications">Las notificaciones están desactivadas, no recibirá notificaciones de vinculación entrantes.</string>
|
||||
|
@@ -18,6 +18,7 @@
|
||||
<string name="pref_plugin_clipboard_sent">Arbelekoa bidali da</string>
|
||||
<string name="pref_plugin_mousepad">Urrutiko sarrera</string>
|
||||
<string name="pref_plugin_mousepad_desc">Erabili zure telefonoa edo tableta ukimen-sagu eta teklatu gisa</string>
|
||||
<string name="pref_plugin_presenter">Aurkezpenetarako urruneko agintea</string>
|
||||
<string name="pref_plugin_presenter_desc">Erabili zure gailua aurkezpen bateko diapositibak aldatzeko</string>
|
||||
<string name="pref_plugin_remotekeyboard">Jaso urruneko tekla-sakatzeak</string>
|
||||
<string name="pref_plugin_remotekeyboard_desc">Jaso tekla-sakatze gertaerak urruneko gailuetatik</string>
|
||||
@@ -51,6 +52,7 @@
|
||||
<string name="remotekeyboard_multiple_connections">Urruneko teklatuekin konexio bat baino gehiago dago, hautatu konfiguratu beharreko gailua</string>
|
||||
<string name="open_mousepad">Urruneko sarrera</string>
|
||||
<string name="mousepad_info">Mugitu hatz bat pantailan zehar saguaren erakuslea mugitzeko. Egin tak klik baterako, eta erabili bi/hiru hatz eskuin eta erdiko botoietarako. Erabili 2 hatz kiribiltzeko. Erabili sakatze luze bat arrastatu eta jaregiteko. Saguaren giroskopio funtzionalitatea pluginen hobespenetatik gaitu daiteke.</string>
|
||||
<string name="mousepad_info_no_gestures">Mugitu hatz bat pantailan kurtsorea mugitzeko, egin tak klik egiteko.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Parekatutako gailuak ez du teklatuko sarreraren euskarririk</string>
|
||||
<string name="mousepad_single_tap_settings_title">Ezarri hatz bakarrarekin tak egitearen ekintza</string>
|
||||
<string name="mousepad_double_tap_settings_title">Ezarri bi hatzez tak egitearen ekintza</string>
|
||||
@@ -119,6 +121,8 @@
|
||||
<string name="my_device_fingerprint">Zure gailuaren ziurtagiriaren SHA256 hatz-marka:</string>
|
||||
<string name="remote_device_fingerprint">Urruneko gailuaren ziurtagiriaren SHA256 hatz-marka hau da:</string>
|
||||
<string name="pair_requested">Parekatzea eskatu da</string>
|
||||
<string name="pair_succeeded">Parekatze arrakastatsua</string>
|
||||
<string name="pairing_request_from">\'%1s\'(e)ren parekatzeko eskaria</string>
|
||||
<plurals name="incoming_file_title">
|
||||
<item quantity="one">%2$s(e)tik fitxategi %1$d jasotzen</item>
|
||||
<item quantity="other">%2$s(e)tik %1$d fitxategi jasotzen</item>
|
||||
@@ -155,6 +159,7 @@
|
||||
<string name="received_file_text">Tak egin \'%1s\' irekitzeko</string>
|
||||
<string name="cannot_create_file">Ezin da sortu %s fitxategia</string>
|
||||
<string name="tap_to_answer">Tak egin erantzuteko</string>
|
||||
<string name="left_click">Bidali ezkerreko klik</string>
|
||||
<string name="right_click">Bidali eskumako klik</string>
|
||||
<string name="middle_click">Bidali erdiko klik</string>
|
||||
<string name="show_keyboard">Erakutsi teklatua</string>
|
||||
@@ -184,6 +189,9 @@
|
||||
<string name="mpris_notifications_explanation">Urruneko euskarria jakinarazpen tiraderan erakusteko jakinarazpen-baimena behar da</string>
|
||||
<string name="mpris_notification_settings_title">Erakutsi euskarri kontrolaren jakinarazpena</string>
|
||||
<string name="mpris_notification_settings_summary">Utzi zure euskarri-jotzaileak kontrolatzen KDE Connect ireki gabe</string>
|
||||
<string name="share_to">Partekatu honekin...</string>
|
||||
<string name="unreachable_device">%s (eskuraezin)</string>
|
||||
<string name="unreachable_device_url_share_text">Gailu eskuraezinekin partekatutako URLak, hartara bidaliko dira eskuragarri dagoenean.\n\n</string>
|
||||
<string name="protocol_version_newer">Gailu honek protokoloaren bertsio berriago bat erabiltzen du</string>
|
||||
<string name="plugin_settings_with_name">%s ezarpenak</string>
|
||||
<string name="invalid_device_name">Gailuaren izen baliogabea</string>
|
||||
@@ -381,6 +389,8 @@
|
||||
<string name="send_compose">Bidali</string>
|
||||
<string name="compose_send_title">Bidalketa osatu</string>
|
||||
<string name="open_compose_send">Konposatu testua</string>
|
||||
<string name="double_tap_to_drag">Tak bikoitza arrastatzeko</string>
|
||||
<string name="hold_to_drag">Eutsi arrastatzeko</string>
|
||||
<string name="about_kde_about">"<h1>Honi buruz</h1> <p>KDE <a href=https://www.gnu.org/philosophy/free-sw.html>Software Askearen</a> garapenarekin engaiatutako mundu osoko software ingeniari, artista, idazle, itzultzaile eta sortzaile elkarte bat da. KDEk Plasma mahaigain ingurunea, ehunka aplikazio, eta haiei euskarria ematen dieten liburutegi ugariak ekoizten ditu. </p> <p>KDE ekimen kooperatibo bat da: ez dago bere norabidea eta produktuak kontrolatzen dituen erakunderik. Aldiz, elkarrekin lan egiten dugu guztiok partekatzen dugun helburu bera lortzeko, munduko Software Aske bikainena eraikitzearena alegia. Jende oro ongi etorria da KDErekin <a href=https://community.kde.org/Get_Involved>elkartu eta laguntza ematera</a>, zu barne. </p> Bisitatu <a href=https://www.kde.org/>https://www.kde.org/</a> , KDE elkartearen eta ekoizten dugun softwarearen gaineko informazio zabalagoa eskuratzeko."</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Akatsen edo nahien berri ematea</h1> <p>Softwarea beti hobetu daiteke, eta KDE taldea horretarako prest dago. Hala ere, zuk - erabiltzailea zaren horrek - zerbait behar bezala ez dabilenean edo hobeto egin daitekeenean esan egin behar diguzu.</p> <p>KDEk programa-akatsen gaineko jarraipena egiteko sistema bat du. Bisitatu <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> edo erabili Honi buruz pantailako «Akatsa jakinarazi» botoia.</p> Hobetzeko iradokizunik baduzu, akatsen jarraipen sisteman zure nahia erregistratzera gonbidatzen zaitugu. Larritasun maila bezala \"Wishlist\" (nahien zerrenda) erabil ezazu horretarako.</string>
|
||||
<string name="about_kde_join_kde">"<h1>Zatoz KDEra</h1> <p>Ez duzu software garatzailea izan behar KDEren taldeko kide izateko. Programen interfazeak itzultzen dituzten hizkuntzen taldeetara batu zaitezke. Grafikoak, gaiak, soinuak, eta dokumentazio hobetua eskain ditzakezu. Zeuk erabaki!</p> <p>Bisitatu <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> parte hartu dezakezun proiektuen informazioa eskuratzeko.</p> Informazio edo dokumentazio gehiago behar baduzu, <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> bisitatuz behar duzuna eskuratuko duzu."</string>
|
||||
@@ -395,6 +405,7 @@
|
||||
<string name="maxim_leshchenko_task">Erabiltzaile-interfazean hobekuntzak eta Honi buru orria hau</string>
|
||||
<string name="holger_kaelberer_task">Urruneko teklatuaren plugina eta akatsen konponketa</string>
|
||||
<string name="saikrishna_arcot_task">Urruneko sarrerako pluginean teklatua erabiltzeko euskarria, akatsen konponketa eta hobekuntza orokorrak</string>
|
||||
<string name="shellwen_chen_task">SFTPren segurtasuna hobetu, proiektu honen mantentze-gaitasuna hobetu, akatsak konpondu eta hobekuntza orokorrak</string>
|
||||
<string name="everyone_else">Urteetan KDE Connect-ekin lagundu duten gainerako guztiak</string>
|
||||
<string name="send_clipboard">Bidali arbelekoa</string>
|
||||
<string name="tap_to_execute">Tak egin exekutatzeko</string>
|
||||
@@ -403,4 +414,8 @@
|
||||
<string name="receive_notifications_permission_explanation">Jakinarazpenak baimendu behar dira beste gailuetatik haiek jasotzeko</string>
|
||||
<string name="findmyphone_notifications_explanation">Aplikazioa atzeko planoan dagoenean telefonoak jo dezan jakinarazpen-baimena behar da</string>
|
||||
<string name="no_notifications">Jakinarazpenak ezgaituta daude, ez duzu jasoko parekatzeko sarrerako jakinarazpenik.</string>
|
||||
<string name="mpris_keepwatching">Jarraitu jotzen</string>
|
||||
<string name="mpris_keepwatching_settings_title">Jarraitu jotzen</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Hedabidea itxi ondoren, gailu honetan jotzen jarraitzeko jakinarazpen ixil bat erakutsi.</string>
|
||||
<string name="notification_channel_keepwatching">Jarraitu jotzen</string>
|
||||
</resources>
|
||||
|
@@ -53,7 +53,7 @@
|
||||
<string name="open_mousepad">Contrôle distant</string>
|
||||
<string name="mousepad_info">Faites glisser votre doigt sur l\'écran pour déplacer le pointeur de la souris. Tapotez pour cliquer et utilisez deux / trois doigts pour les clics droit et centre. Utilisez 2 doigts pour faire un défilement. Faites un appui prolongé pour réaliser un glisser-déposer. La fonctionnalité de gyroscope de souris peut être activée à partir des préférences de module externe.</string>
|
||||
<string name="mousepad_info_no_gestures">Faites glisser un doigt sur l\'écran pour déplacer le pointeur de souris. Tapotez sur l\'écran pour effectuer un clic.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">La saisie par le clavier n\'est pas pris en charge par le périphérique appairée.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">La saisie par le clavier n\'est pas pris en charge par le périphérique associé.</string>
|
||||
<string name="mousepad_single_tap_settings_title">Définir une action pour tapotage avec un doigt</string>
|
||||
<string name="mousepad_double_tap_settings_title">Action pour l\'appui à deux doigts</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Action pour l\'appui à trois doigts</string>
|
||||
@@ -99,7 +99,7 @@
|
||||
<string name="pref_plugin_mousepad_send_keystrokes">Envoyez comme appuis de touches</string>
|
||||
<string name="mouse_receiver_plugin_description">Recevoir les mouvements de la souri distante</string>
|
||||
<string name="mouse_receiver_plugin_name">Récepteur de souris</string>
|
||||
<string name="mouse_receiver_no_permissions">Pour recevoir des entrées tactiles à distance, vous devez accorder des autorisations d’accessibilité pour contrôler entièrement votre périphérique</string>
|
||||
<string name="mouse_receiver_no_permissions">Pour recevoir des entrées tactiles à distance, vous devez accorder des autorisations d\'accessibilité pour contrôler entièrement votre périphérique</string>
|
||||
<string name="view_status_title">État</string>
|
||||
<string name="battery_status_format">Batterie : %d %%</string>
|
||||
<string name="battery_status_low_format">Batterie : %d %% Batterie faible</string>
|
||||
@@ -186,12 +186,12 @@
|
||||
<item>1 minute</item>
|
||||
<item>2 minutes</item>
|
||||
</string-array>
|
||||
<string name="mpris_notifications_explanation">Les permissions pour les notifications sont nécessaires pour afficher des supports distants dans le panneau des notifications</string>
|
||||
<string name="mpris_notifications_explanation">Les permissions pour les notifications sont nécessaires pour afficher des média distants dans le panneau des notifications.</string>
|
||||
<string name="mpris_notification_settings_title">Afficher la notification de contrôle du lecteur multimédia</string>
|
||||
<string name="mpris_notification_settings_summary">Vous permet de contrôler vos lecteurs multimédia sans ouvrir KDEConnect.</string>
|
||||
<string name="share_to">Partager vers…</string>
|
||||
<string name="unreachable_device">%s (Inaccessible)</string>
|
||||
<string name="unreachable_device_url_share_text">Les URL partagées vers un appareil inaccessible lui seront transmises une fois qu’il re-deviendra accessible.\n\n</string>
|
||||
<string name="unreachable_device_url_share_text">Les URL partagées vers un appareil inaccessible lui seront transmises une fois qu\'il re-deviendra accessible.\n\n</string>
|
||||
<string name="protocol_version_newer">Le périphérique utilise une version plus récente du protocole</string>
|
||||
<string name="plugin_settings_with_name">Configuration %s</string>
|
||||
<string name="invalid_device_name">Nom de périphérique non valable</string>
|
||||
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Envoyer</string>
|
||||
<string name="compose_send_title">Préparer l\'envoi</string>
|
||||
<string name="open_compose_send">Composer du texte</string>
|
||||
<string name="double_tap_to_drag">Tapotement double pour un glisser</string>
|
||||
<string name="hold_to_drag">Maintenir pour faire glisser</string>
|
||||
<string name="about_kde_about"><h1>A propos</h1> <p>KDE est une communauté mondiale d\'ingénieurs en logiciel, d\'artistes d\'ingénieurs logiciels, d\'artistes, d\'écrivains, de traducteurs et de créateurs s\'engageant pour le développement de <a href=https://www.gnu.org/philosophy/free-sw.html> Logiciels libres </a >. KDE développe l\'environnement de bureau Plasma, des centaines d\'applications, et les nombreuses bibliothèques logicielles les prenant en charge. KDE est une entreprise coopérative : aucune entité centrale ne contrôle sa direction ou ses produits. Au contraire, nous travaillons tous ensemble pour atteindre un objectif commun : construire le meilleur logiciel libre au monde. Tout le monde est est le bienvenu pour <a href=https://community.kde.org/Get_Involved>rejoindre et contribuer</a> à KDE, y compris vous. </p> Visitez <a href=https://www.kde.org/>https://www.kde.org/</a> pour de plus amples informations sur la communauté KDE et les logiciels que nous développons.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Signaler des bogues ou des souhaits</h1> <p> Les logiciels peuvent toujours être améliorés et l\'équipe KDE est prête à le faire. Cependant, vous - la personne utilisatrice - devez nous dire quand quelque chose ne fonctionne pas comme prévu ou pourrait être mieux fait. KDE dispose d\'un système de suivi des bogues. Visitez <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> ou utilisez le bouton bouton « Signaler un bogue » de la page « A propos » pour signaler les bogues. Si vous avez une suggestion d\'amélioration, vous pouvez aussi utiliser le système de suivi des bogues pour enregistrer votre souhait. Veuillez vous assurer de bien utiliser le niveau de gravité appelée « Liste de souhaits ».</string>
|
||||
<string name="about_kde_join_kde"><h1>Rejoignez KDE</h1> <p>Vous n\'avez pas besoin d\'être un développeur de logiciels pour être membre de l\'équipe de KDE. Vous pouvez rejoindre les équipes nationales qui traduisent les interfaces des programmes. Vous pouvez fournir des illustrations, des thèmes, des sons, et de la documentation améliorée. À vous de décider ! </p> <p>Visitez la page <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> pour obtenir des informations sur certains projets auxquels vous pouvez participer.</p> Si vous avez besoin de plus d\'informations ou de documentation, veuillez visitez la page <a href=https://techbase.kde.org/>https://techbase.kde.org/</a>, qui vous fournira ce dont vous avez besoin.</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Améliorations de l\'interface utilisateur et de cette page d\'à propos</string>
|
||||
<string name="holger_kaelberer_task">Corrections du module externe de clavier sans fil et de bogues</string>
|
||||
<string name="saikrishna_arcot_task">Prise en charge de l\'utilisation du clavier dans le module d\'entrée à distance, corrections de bogues et améliorations générales</string>
|
||||
<string name="everyone_else">"Toutes les autres personnes ayant contribué KDEConnect depuis plusieurs années"</string>
|
||||
<string name="shellwen_chen_task">Implémentation de la sécurité de SFTP, amélioration de la maintenabilité de ce projet, corrections de bogues et améliorations générales</string>
|
||||
<string name="everyone_else">Toutes les autres personnes ayant contribué à KDEConnect depuis plusieurs années</string>
|
||||
<string name="send_clipboard">Envoyer le presse-papier</string>
|
||||
<string name="tap_to_execute">Tapotez pour lancer</string>
|
||||
<string name="plugin_stats">Statistiques des modules externes</string>
|
||||
<string name="enable_udp_broadcast">Activer la découverte de périphériques « UDP »</string>
|
||||
<string name="enable_bluetooth">Activer l\'interface Bluetooth (Bêta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Les notifications doivent être autorisées pour en recevoir d\'autres périphériques</string>
|
||||
<string name="findmyphone_notifications_explanation">Les permissions pour les notifications sont nécessaires pour que le téléphone puisse sonner lorsque l\'application s\'exécute en tâche de fond</string>
|
||||
<string name="no_notifications">Les notifications sont désactivées,. Vous ne recevrez pas de notifications d\'appairages entrants.</string>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Enviar</string>
|
||||
<string name="compose_send_title">Preparar un envío</string>
|
||||
<string name="open_compose_send">Escribir texto</string>
|
||||
<string name="double_tap_to_drag">Toque dúas veces para arrastrar</string>
|
||||
<string name="hold_to_drag">Manteña para arrastrar</string>
|
||||
<string name="about_kde_about">"<h1>Sobre</h1> <p>KDE é unha comunidade internacional de persoas dedicadas á enxeñaría de soporte lóxico, á arte, á documentación, á tradución e á creación, todas elas comprometidas co desenvolvemento de <a href=https://www.gnu.org/philosophy/free-sw.html>soporte lóxico libre</a>. KDE produce o contorno de escritorio Plasma, centos de aplicacións, e as moitas bibliotecas de soporte lóxico sobre as que estas están construídas.</p> <p>KDE é un esforzo cooperativo: non hai unha única entidade que controle a súa dirección ou os seus produtos. No seu lugar, xuntámonos para traballar no obxectivo común de construír o mellor soporte lóxico libre do mundo. Todas as persoas son benvidas a <a href=https://community.kde.org/Get_Involved>unirse e colaborar</a> en KDE, incluída vostede.</p> Visite <a href=https://www.kde.org/>https://www.kde.org/</a> para máis información sobre a comunidade KDE e o soporte lóxico que produce."</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Informe de fallos ou pida melloras</h1> <p>O software sempre pode mellorarse, e o equipo de KDE está preparado para facelo. Porén, vostede, a persoa usuaria, ten que avisarnos cando algo non funciona como espera ou podería mellorarse.</p> <p>KDE ten un sistema de seguimento de fallos. Visite <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> ou use o botón de «Informar dun fallo» da pantalla de información para informar dun fallo.</p> Se ten unha suxestión de mellora tamén pode usar o sistema de seguimento de fallos para rexistrala. Asegúrese nese caso de usar a severidade «Lista de desexos».</string>
|
||||
<string name="about_kde_join_kde"><h1>Únase a KDE</h1> <p>Non necesita saber desenvolver software para formar parte do equipo de KDE. Pode unirse aos equipos de idiomas que traducen as interfaces dos programas. Pode fornecer imaxes, temas, sons, e mellorar a documentación. Vostede decide!</p> <p>Visite <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> para informarse sobre os proxectos nos que pode participar.</p> Se necesita máis información ou documentación, ten o que necesita en <a href=https://techbase.kde.org/>https://techbase.kde.org/</a>.</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Melloras na UI e nesta páxina de información</string>
|
||||
<string name="holger_kaelberer_task">Complemento de teclado remoto e correccións de fallos</string>
|
||||
<string name="saikrishna_arcot_task">Posibilidade de usar o teclado no complemento de entrada remota, correccións de fallos e melloras xerais</string>
|
||||
<string name="shellwen_chen_task">Mellorar a seguridade de SFTP, facilitar o mantemento do proxecto, correccións de fallos e melloras xerais.</string>
|
||||
<string name="everyone_else">O resto de xente que colaborou en KDE Connect ao longo dos anos</string>
|
||||
<string name="send_clipboard">Enviar o portapapeis</string>
|
||||
<string name="tap_to_execute">Toque para executar</string>
|
||||
<string name="plugin_stats">Estatísticas do complemento</string>
|
||||
<string name="enable_udp_broadcast">Activar o descubrimento de dispositivos UDP.</string>
|
||||
<string name="enable_bluetooth">Activar o Bluetooth (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Debe permitir notificacións para recibilas doutros dispositivos.</string>
|
||||
<string name="findmyphone_notifications_explanation">Necesita o permiso de notificacións para que o teléfono poda soar cando a aplicación está en segundo plano.</string>
|
||||
<string name="no_notifications">As notificacións están desactivadas, non recibirá notificacións entrantes de emparellamento.</string>
|
||||
|
@@ -201,4 +201,7 @@
|
||||
<string name="receive_notifications_permission_explanation">Notificationes necessita esser permettite a reciper los ex altere dispositivos</string>
|
||||
<string name="findmyphone_notifications_explanation">Le permission de notificationes es necessari assi que le telephono pote sonar quando le app es in le fundo</string>
|
||||
<string name="no_notifications">Notificatione es dishabilitate, tu nonrecipera notificatioones de association in arrivata.</string>
|
||||
<string name="mpris_keepwatching">"Continua a executar "</string>
|
||||
<string name="mpris_keepwatching_settings_title">Continua a executar</string>
|
||||
<string name="notification_channel_keepwatching">Continua a executar</string>
|
||||
</resources>
|
||||
|
@@ -307,7 +307,7 @@
|
||||
<string name="settings_rename">Nome dispositivo</string>
|
||||
<string name="settings_dark_mode">Tema scuro</string>
|
||||
<string name="settings_more_settings_title">Altre impostazioni</string>
|
||||
<string name="settings_more_settings_text">Le impostazioni per dispositivo sono disponibili sotto «Impostazione estensioni» dall\'interno del dispositivo.</string>
|
||||
<string name="settings_more_settings_text">Le impostazioni per dispositivo sono disponibili sotto «Impostazioni estensioni» dall\'interno del dispositivo.</string>
|
||||
<string name="setting_persistent_notification">Mostra notifica persistente</string>
|
||||
<string name="setting_persistent_notification_oreo">Notifica persistente</string>
|
||||
<string name="setting_persistent_notification_description">Tocca per abilitare/disabilitare nelle impostazioni delle notifiche</string>
|
||||
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Invia</string>
|
||||
<string name="compose_send_title">Invio scorciatoia composita</string>
|
||||
<string name="open_compose_send">Componi il testo</string>
|
||||
<string name="double_tap_to_drag">Doppio tocco per trascinare</string>
|
||||
<string name="hold_to_drag">Tieni premuto per trascinare</string>
|
||||
<string name="about_kde_about"><h1>Informazioni</h1> <p>KDE è una comunità mondiale di ingegneri del software, artisti, scrittori, traduttori e creatori che si impegnano a sviluppare <a href=https://www.gnu.org/philosophy/free-sw.html>software libero</a>. KDE produce l\'ambiente desktop Plasma, centinaia di applicazioni e le numerose librerie software che le supportano.</p> <p>KDE è un\'impresa cooperativa: nessuna singola entità ne controlla la direzione o i prodotti. Invece, lavoriamo insieme per raggiungere l\'obiettivo comune di costruire il miglior software libero del mondo. Tutti sono invitati a <a href=https://community.kde.org/Get_Involved>unirsi e contribuire</a> a KDE, incluso te.</p> Visita <a href=https://www.kde.org/>https://www.kde.org/</a> per ulteriori informazioni sulla comunità KDE e sul software che produciamo.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Segnala bug o desideri</h1> <p>Il software può sempre essere migliorato e il team di KDE è pronto a farlo. Tuttavia, tu - l\'utente - devi dirci quando qualcosa non funziona come previsto o potrebbe essere fatto meglio.</p> <p>KDE ha un sistema di tracciamento dei bug. Visita <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> oppure utilizza il pulsante «Segnala bug» dalla schermata delle informazioni per segnalare i bug.</p> Se hai un suggerimento per il miglioramento, puoi utilizzare il sistema di tracciamento dei bug per registrare il tuo desiderio. Assicurati di utilizzare «Wishlist» per il campo Severity.</string>
|
||||
<string name="about_kde_join_kde"><h1>Unisciti a KDE</h1> <p>Non devi essere uno sviluppatore di software per essere un membro della squadra di KDE. Puoi unirti ai gruppi linguistici che traducono le interfacce dei programmi. Puoi fornire grafica, temi, suoni e documentazione migliorata. Decidi tu!</p> <p>Visita <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> per informazioni su alcuni progetti a cui puoi partecipare.</p> Se hai bisogno di ulteriori informazioni o di documentazione, visita <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> ti fornirà quello che ti serve.</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Miglioramenti all\'interfaccia utente e questa pagina informativa</string>
|
||||
<string name="holger_kaelberer_task">Estensione della tastiera remota e correzioni di bug</string>
|
||||
<string name="saikrishna_arcot_task">Supporto per l\'utilizzo della tastiera nell\'estensione di inserimento remoto, correzioni di bug e miglioramenti generali</string>
|
||||
<string name="shellwen_chen_task">Migliora la sicurezza di SFTP, migliora la manutenibilità di questo progetto, correzioni di bug e miglioramenti generali</string>
|
||||
<string name="everyone_else">Tutti gli altri che hanno contribuito a KDE Connect nel corso degli anni</string>
|
||||
<string name="send_clipboard">Invia gli appunti</string>
|
||||
<string name="tap_to_execute">Tocca per eseguire</string>
|
||||
<string name="plugin_stats">Statistiche delle estensioni</string>
|
||||
<string name="enable_udp_broadcast">Abilita rilevamento UDP dei dispositivi</string>
|
||||
<string name="enable_bluetooth">Abilita bluetooth (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Le notifiche devono essere consentite per riceverle da altri dispositivi</string>
|
||||
<string name="findmyphone_notifications_explanation">L\'autorizzazione alle notifiche è necessaria affinché il telefono possa squillare quando l\'applicazione è sullo sfondo</string>
|
||||
<string name="no_notifications">Le notifiche sono disabilitate, non riceverai notifiche di associazione in arrivo.</string>
|
||||
|
@@ -405,6 +405,8 @@
|
||||
<string name="send_compose">שליחה</string>
|
||||
<string name="compose_send_title">שליחה מחיבור</string>
|
||||
<string name="open_compose_send">טקסט חיבור</string>
|
||||
<string name="double_tap_to_drag">נקישה כפולה לגרירה</string>
|
||||
<string name="hold_to_drag">החזקה לגרירה</string>
|
||||
<string name="about_kde_about"><h1>על אודות</h1> <p>KDE היא קהילה בינלאומית של מהנדסי תוכנה, כותבים, מתרגמים ויוצרים שמחויבים לפיתוח <a href=https://www.gnu.org/philosophy/free-sw.html>תוכנה חופשית</a>. מיזם KDE מפיק את סביבת שולחן העבודה פלזמה, מאות יישומים ומגוון רחב של ספריות תוכנה כדי לתמוך בהם.</p> <p>KDE הוא תאגיד חברתי: אף ישות יחידה לא שולטת בכיוון או במוצרים. במקום, אנו פעולים יחד כדי להגיע ליעדנו המשותף: לבנות את התוכנה החופשית הטובה בעולם. כולם מוזמנים <a href=https://community.kde.org/Get_Involved>להצטרף ולתרום</a> ל־KDE, כולל אותך.</p> כדאי לבקר באתר <a href=https://www.kde.org/>https://www.kde.org/</a> למידע נוסף על קהילת KDE ועל התוכנה שאנו מפיקים.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>דיווח על תקלות או משאלות</h1> <p>תוכנות תמיד יכולות להשתפר וצוות KDE ערוך לכך. עם זאת, אנו זקוקים לך - המשתמש או המשתמשת - כדי לספר לנו מתי משהו לא עובד כצפוי או עשוי לעבוד טוב יותר.</p> <p>ל־KDE יש מערכת מעקב אחר תקלות. יש לבקר ב־<a href=https://bugs.kde.org/>https://bugs.kde.org/</a> או להשתמש בכפתור „דיווח על תקלה” ממסך על אודות כדי לדווח על תקלות.</p> אם יש לך הצעה לשיפור, אנו מזמינים אותך להשתמש במערכת מעקב התקלות כדי לתעד את המשאלה שלך, חשוב לזכור לסמן אותה בדרגת חומרה (severity) בשם „Wishlist” (רשימת משאלות).</string>
|
||||
<string name="about_kde_join_kde"><h1>הצטרפות ל־KDE</h1> <p>לא צריך להיות מפתחי תוכנה כדי להצטרף לחברות ב־KDE. אפשר להצטרף לצוותים המקומיים שמתרגמים את ממשקי התוכנות. אפשר לספק גרפיקה, ערכות עיצוב, צלילי ושיפור התיעוד. הבחירה היא רק שלך!</p> <p>יש לבקר ב־<a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> למידע על חלק מהמיזמים בהם ניתן לקחת חלק.</p> כדי לקבל מידע או תיעוד נוספים, ביקור ב־<a href=https://techbase.kde.org/>https://techbase.kde.org/</a> יספק לך את מה שדרוש לך.</string>
|
||||
@@ -419,6 +421,7 @@
|
||||
<string name="maxim_leshchenko_task">שיפורי ממשק משתמש ועמוד על אודות הזה</string>
|
||||
<string name="holger_kaelberer_task">תוסף מקלדת מרוחקת ותיקוני תקלות</string>
|
||||
<string name="saikrishna_arcot_task">תמיכה בשימוש במקלדת בתוסף הקלט המרוחק, תיקוני תקלות ושיפורים כלליים</string>
|
||||
<string name="shellwen_chen_task">שיפור האבטחה ב־SFTP, שיפור תחזוקתיות המיזם הזה, תיקוני תקלות ושיפורים כלליים</string>
|
||||
<string name="everyone_else">כל מי שתרם ל־KDE Connect לאורך השנים</string>
|
||||
<string name="send_clipboard">שליחת לוח גזירים</string>
|
||||
<string name="tap_to_execute">נגיעה תפעיל</string>
|
||||
|
@@ -18,6 +18,7 @@
|
||||
<string name="pref_plugin_clipboard_sent">ბუფერი გაგზავნილია</string>
|
||||
<string name="pref_plugin_mousepad">დაშორებული შეყვანა</string>
|
||||
<string name="pref_plugin_mousepad_desc">გამოიყენეთ თქვენი ტელეფონი ან ტაბლეტი როგორც თაჩპედი და კლავიატურა</string>
|
||||
<string name="pref_plugin_presenter">დაშორებული პრეზენტაცია</string>
|
||||
<string name="pref_plugin_presenter_desc">გამოიყენეთ თქვენი მოწყობილობა სლაიდშოუს სამართავად</string>
|
||||
<string name="pref_plugin_remotekeyboard">დაშორებული ღილაკების მიღება</string>
|
||||
<string name="pref_plugin_mpris">მულტიმედიის მართვა</string>
|
||||
@@ -92,6 +93,7 @@
|
||||
<string name="error_canceled_by_other_peer">გაუქმებულია პარტნიორის მიერ</string>
|
||||
<string name="encryption_info_title">დაშიფვრის ინფორმაცია</string>
|
||||
<string name="pair_requested">დაწყვილების მოთხოვნა</string>
|
||||
<string name="pair_succeeded">დაწყვილება წარმატებულია</string>
|
||||
<plurals name="incoming_files_text">
|
||||
<item quantity="one">File: %1s</item>
|
||||
<item quantity="other">(File %2$d of %3$d) : %1$s</item>
|
||||
@@ -104,6 +106,7 @@
|
||||
<string name="received_file_text">\'%1s\'-ის გასახსნელად დაატყაპუნეთ</string>
|
||||
<string name="cannot_create_file">ფაილის (%s) შექმნის შეცდომა</string>
|
||||
<string name="tap_to_answer">საპასუხოდ დაატყაპუნეთ</string>
|
||||
<string name="left_click">მარჯვენა წკაპის გაგზავნა</string>
|
||||
<string name="right_click">მარჯვენა წკაპის გაგზავნა</string>
|
||||
<string name="middle_click">შუა წკაპის გაგზავნა</string>
|
||||
<string name="show_keyboard">კლავიატურის ჩვენება</string>
|
||||
@@ -129,6 +132,8 @@
|
||||
<item>1 წუთი</item>
|
||||
<item>2 წუთი</item>
|
||||
</string-array>
|
||||
<string name="share_to">გაზიარება…</string>
|
||||
<string name="unreachable_device">%s (მიუწვდომელია)</string>
|
||||
<string name="protocol_version_newer">მოწყობილობა პროტოკოლის უფრო ახალ ვერსიას იყენებს</string>
|
||||
<string name="plugin_settings_with_name">%s-ის მორგება</string>
|
||||
<string name="invalid_device_name">მოწყობილობის არასწორი სახელი</string>
|
||||
|
@@ -18,6 +18,7 @@
|
||||
<string name="pref_plugin_clipboard_sent">클립보드 보냄</string>
|
||||
<string name="pref_plugin_mousepad">원격 입력</string>
|
||||
<string name="pref_plugin_mousepad_desc">내 휴대폰이나 태블릿을 터치패드와 키보드로 사용하기</string>
|
||||
<string name="pref_plugin_presenter">프레젠테이션 리모콘</string>
|
||||
<string name="pref_plugin_presenter_desc">내 장치로 프레젠테이션 슬라이드 전환하기</string>
|
||||
<string name="pref_plugin_remotekeyboard">원격 키 입력 받기</string>
|
||||
<string name="pref_plugin_remotekeyboard_desc">원격 장치의 키 입력 이벤트 받기</string>
|
||||
@@ -51,6 +52,7 @@
|
||||
<string name="remotekeyboard_multiple_connections">원격 키보드 연결이 여러 개 있습니다. 설정할 장치를 선택하십시오</string>
|
||||
<string name="open_mousepad">원격 입력</string>
|
||||
<string name="mousepad_info">화면에서 손가락을 움직이면 마우스 커서를 움직입니다. 화면을 누르면 왼쪽 단추를 누르고, 두 손가락과 세 손가락으로 누르면 오른쪽/가운데 단추를 누릅니다. 두 손가락을 사용하여 스크롤할 수 있습니다. 드래그 앤 드롭을 사용하려면 길게 누르십시오. 플러그인 설정에서 자이로 마우스를 활성화할 수 있습니다</string>
|
||||
<string name="mousepad_info_no_gestures">화면에서 손가락을 움직이면 마우스 커서를 움직이며, 탭하면 클릭합니다.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">페어링된 장치에서 키보드 입력을 지원하지 않음</string>
|
||||
<string name="mousepad_single_tap_settings_title">한 손가락으로 눌렀을 때 동작 설정</string>
|
||||
<string name="mousepad_double_tap_settings_title">두 손가락으로 눌렀을 때 동작 설정</string>
|
||||
@@ -119,6 +121,8 @@
|
||||
<string name="my_device_fingerprint">내 장치 인증서의 SHA256 지문:</string>
|
||||
<string name="remote_device_fingerprint">원격 장치 인증서의 SHA256 지문:</string>
|
||||
<string name="pair_requested">연결 요청됨</string>
|
||||
<string name="pair_succeeded">연결 성공</string>
|
||||
<string name="pairing_request_from">\'%1s\'에서 연결 요청</string>
|
||||
<plurals name="incoming_file_title">
|
||||
<item quantity="other">%2$s에서 보낸 파일 %1$d개 받음</item>
|
||||
</plurals>
|
||||
@@ -147,6 +151,7 @@
|
||||
<string name="received_file_text">\'%1s\'을(를) 열려면 누르십시오</string>
|
||||
<string name="cannot_create_file">파일 %s을(를) 만들 수 없음</string>
|
||||
<string name="tap_to_answer">눌러서 응답하기</string>
|
||||
<string name="left_click">왼쪽 단추 클릭 신호 보내기</string>
|
||||
<string name="right_click">오른쪽 단추 클릭 신호 보내기</string>
|
||||
<string name="middle_click">가운데 단추 클릭 신호 보내기</string>
|
||||
<string name="show_keyboard">키보드 표시</string>
|
||||
@@ -176,6 +181,9 @@
|
||||
<string name="mpris_notifications_explanation">알림 서랍에 원격 미디어를 표시하려면 알림 권한이 필요합니다</string>
|
||||
<string name="mpris_notification_settings_title">미디어 제어 알림 표시</string>
|
||||
<string name="mpris_notification_settings_summary">KDE Connect를 열지 않고 미디어 재생기 제어</string>
|
||||
<string name="share_to">다음으로 공유…</string>
|
||||
<string name="unreachable_device">%s(접근할 수 없음)</string>
|
||||
<string name="unreachable_device_url_share_text">접근할 수 없는 장치와 공유한 URL은 장치에 다시 접근할 수 있게 될 때 전달됩니다.\n\n</string>
|
||||
<string name="protocol_version_newer">이 장치의 프로토콜 버전이 더 새롭습니다</string>
|
||||
<string name="plugin_settings_with_name">%s 설정</string>
|
||||
<string name="invalid_device_name">잘못된 장치 이름</string>
|
||||
@@ -387,6 +395,7 @@
|
||||
<string name="maxim_leshchenko_task">UI 개선과 정보 페이지</string>
|
||||
<string name="holger_kaelberer_task">원격 키보드 플러그인과 버그 수정</string>
|
||||
<string name="saikrishna_arcot_task">원격 입력 플러그인에서 키보드 사용 지원, 버그 수정과 개선</string>
|
||||
<string name="shellwen_chen_task">SFTP 보안 개선, 프로젝트 관리 편의성 개선, 버그 수정과 개선</string>
|
||||
<string name="everyone_else">그 외 오랫동안 KDE Connect에 기여한 사람들</string>
|
||||
<string name="send_clipboard">클립보드 보내기</string>
|
||||
<string name="tap_to_execute">실행하려면 누르십시오</string>
|
||||
@@ -395,4 +404,8 @@
|
||||
<string name="receive_notifications_permission_explanation">다른 장치에서 알림을 받으려면 알림을 허용해야 합니다</string>
|
||||
<string name="findmyphone_notifications_explanation">앱이 백그라운드에서 실행 중일 때 장치를 울리게 하려면 알림 권한이 필요합니다</string>
|
||||
<string name="no_notifications">알림이 비활성화되어 있습니다. 들어오는 페어링 알림을 받을 수 없습니다</string>
|
||||
<string name="mpris_keepwatching">계속 재생</string>
|
||||
<string name="mpris_keepwatching_settings_title">계속 재생</string>
|
||||
<string name="mpris_keepwatching_settings_summary">미디어를 닫은 후 이 장치에서 계속 재생할 수 있는 조용한 알림 표시</string>
|
||||
<string name="notification_channel_keepwatching">계속 재생</string>
|
||||
</resources>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Verzenden</string>
|
||||
<string name="compose_send_title">Opstellen van verzending</string>
|
||||
<string name="open_compose_send">Tekst opstellen</string>
|
||||
<string name="double_tap_to_drag">Dubbel tikken om te slepen</string>
|
||||
<string name="hold_to_drag">Ingedrukt houden om te slepen</string>
|
||||
<string name="about_kde_about"><h1>Info over</h1> <p>KDE is een wereldwijde gemeenschap van software ingenieurs, artiesten, schrijvers, vertalers en makers die toegewijd zijn aan <a href=https://www.gnu.org/philosophy/free-sw.html>Vrije software</a> ontwikkeling. KDE produceert de Plasma bureaubladomgeving, honderden toepassingen en de vele software bibliotheken die deze ondersteunen.</p> <p>KDE is een coöperatieve onderneming: geen enkele entiteit controleert zijn richting of producten. In plaats daarvan werken we samen om het gemeenschappelijke doel te bereiken van het bouwen van de \'s werelds mooiste Vrije software. Iedereen is welkom om <a href=https://community.kde.org/Get_Involved>mee te doen en bij te dragen</a> aan KDE, inclusief u.</p> Bezoek <a href=https://www.kde.org/>https://www.kde.org/</a> voor meer informatie over de KDE gemeenschap en de software die we produceren.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Bugs of wensen rapporteren</h1> <p>Software kan altijd verbeterd worden en het KDE team is gereed om dat te doen. Echter, u - de gebruiker - moet ons vertellen wanneer iets niet werkt zoals verwacht of beter gedaan kan worden.</p> <p>KDE heeft een bugvolgsysteem. Bezoek <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> of gebruik de knop \"Bug rapporteren\" uit het Info over scherm om bugs te rapporteren.</p> Als u een suggestie voor verbetering dan bent u welkom om het bugvolgsysteem te gebruiken om uw wens te registreren. Ga na dat u de ernst genaamd \"Wishlist\" gebruikt.</string>
|
||||
<string name="about_kde_join_kde"><h1>Mee doen met KDE</h1> <p>U hoeft geen software ontwerper te zijn om een lid van het KDE-team te worden. U kunt meedoen met een vertaalteam dat interfaces van programma\'s vertaalt. U kunt illustraties, thema\'s, geluiden en verbeterde documentatie leveren. U beslist!</p> <p>Bezoek <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> voor informatie over projecten waarin u kunt participeren.</p> Als u meer informatie of documentatie nodig hebt, dan kan een bezoek aan <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> u alles wat u nodig hebt leveren.</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Verbeteringen aan UI en deze info over pagina</string>
|
||||
<string name="holger_kaelberer_task">"Plug-in voor toetsenbord op afstand en reparaties"</string>
|
||||
<string name="saikrishna_arcot_task">Ondersteuning voor gebruik van toetsenbord in de plug-in voor invoer op afstand, bugreparaties en algemene verbeteringen</string>
|
||||
<string name="shellwen_chen_task">Verbeter de beveiliging van SFTP, verbeter de onderhoudbaarheid van dit project, reparaties van bugs en algemene verbeteringen</string>
|
||||
<string name="everyone_else">Ieder ander die over de jaren heeft bijgedragen aan KDE Connect</string>
|
||||
<string name="send_clipboard">Klembord verzenden</string>
|
||||
<string name="tap_to_execute">Tik om uit te voeren</string>
|
||||
<string name="plugin_stats">Plug-in-statistieken</string>
|
||||
<string name="enable_udp_broadcast">Ontdekken van UDP-apparaat inschakelen</string>
|
||||
<string name="enable_bluetooth">Bluetooth inschakelen (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Meldingen moeten toegestaan worden om ze te ontvangen van andere apparaten</string>
|
||||
<string name="findmyphone_notifications_explanation">Het recht op meldingen is nodig zodat de telefoon af kan gaan wanneer de toepassing in de achtergrond is</string>
|
||||
<string name="no_notifications">Meldingen zijn uitgeschakeld, u zult inkomende paringmeldingen ontvangen.</string>
|
||||
|
@@ -122,6 +122,7 @@
|
||||
<string name="remote_device_fingerprint">SHA-256-fingeravtrykket til fjerneiningssertifikatet er:</string>
|
||||
<string name="pair_requested">Paringsførespurnad</string>
|
||||
<string name="pair_succeeded">Paring fullført</string>
|
||||
<string name="pairing_request_from">Paringsførespurnad frå «%1s»</string>
|
||||
<plurals name="incoming_file_title">
|
||||
<item quantity="one">Fekk %1$d fil frå %2$s</item>
|
||||
<item quantity="other">Fekk %1$d filer frå %2$s</item>
|
||||
@@ -388,6 +389,8 @@
|
||||
<string name="send_compose">Send</string>
|
||||
<string name="compose_send_title">Send tekst</string>
|
||||
<string name="open_compose_send">Skriv tekst</string>
|
||||
<string name="double_tap_to_drag">Dobbelttrykk for å dra</string>
|
||||
<string name="hold_to_drag">Hald for å dra</string>
|
||||
<string name="about_kde_about"><h1>Om</h1> <p>KDE er eit verdsfemnande fellesskap av eldsjeler som programmerer, teiknar, komponerer, dokumenterer, set om eller hjelper til på andre måtar med utvikling av <a href=https://www.gnu.org/philosophy/free-sw.html>fri programvare</a>. Me har laga brukarflata Plasma, hundrevis av program og dei mange programbiblioteka desse byggjer på.</p> <p>KDE er eit fellesskap der inga einskild gruppe, firma eller organisasjon har eigarskap til produkta eller styrer retninga den vidare utviklinga skal gå i. Derimot arbeider me saman om å oppnå vårt felles mål om å laga fri programvare i verdsklasse. Alle er <a href=https://community.kde.org/Get_Involved>velkomne til å bidra</a> – du òg.</p>Du finn meir informasjon om KDE og programma me utviklar på <a href=https://www.kde.org/>https://www.kde.org/</a>.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Meld frå om feil eller ønskje</h1> <p>Ein kan alltid forbetra programvare, og KDE-gruppa arbeider heile tida for det. Men du, som brukar, må melda frå til oss når noko ikkje verkar slik du forventar, eller når noko kunne vore gjort betre.</p> <p>KDE har eit feilsporingssystem. Gå til <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> eller vel «Meld frå om feil» på «Om»-sida for å melda frå om feil.</p> Om du har framlegg til forbetringar, kan du gjerne registrera òg desse i feilsporingssystemet. Sjå då til at du har markert feilrapporten med «Wishlist».</string>
|
||||
<string name="about_kde_join_kde"><h1>Vert med i KDE</h1> <p>Du treng ikkje vera programutviklar for å hjelpa til med KDE. Du kan arbeida med omsetjingar, laga grafikk, tema, lydar eller betre hjelpetekstar. Her er noko for alle!</p> <p>På <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> finn du informasjon om nokre prosjekt du kan delta i.</p> Om du vil ha meir informasjon eller dokumentasjon, finn du det du treng på <a href=https://techbase.kde.org/>https://techbase.kde.org/</a>.</string>
|
||||
@@ -402,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Forbetringar av brukarflata og denne «om»-sida</string>
|
||||
<string name="holger_kaelberer_task">Fjerntastatur-tillegget og feilrettingar</string>
|
||||
<string name="saikrishna_arcot_task">Støtte for bruk av tastaturet i fjernstyringstillegget, feilrettingar og generelle forbetringar</string>
|
||||
<string name="shellwen_chen_task">Forbetra tryggleiken til SFTP, gjort prosjektet lettare å vedlikehalda, feilrettingar og generelle forbetringar</string>
|
||||
<string name="everyone_else">Alle andre som har hjelpt til med utviklinga av KDE Connect opp gjennom åra</string>
|
||||
<string name="send_clipboard">Send utklippstavla</string>
|
||||
<string name="tap_to_execute">Tapp for å utføra handlinga</string>
|
||||
<string name="plugin_stats">Programtillegg-statistikk</string>
|
||||
<string name="enable_udp_broadcast">Slå på einingsoppdaging via UDP</string>
|
||||
<string name="enable_bluetooth">Bruk Bluetooth (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Du må tillata varslingar for å kunna frå varslingar frå andre einingar</string>
|
||||
<string name="findmyphone_notifications_explanation">Varslingsløyvet trengst for at telefonen skal kunna ringja når appen er i bakgrunnsmodus</string>
|
||||
<string name="no_notifications">Varslingar er slåtte av, så du vil ikkje få varsling om paringsførespurnadar.</string>
|
||||
|
@@ -405,6 +405,8 @@
|
||||
<string name="send_compose">Wyślij</string>
|
||||
<string name="compose_send_title">Napisz do wysłania</string>
|
||||
<string name="open_compose_send">Napisz tekst</string>
|
||||
<string name="double_tap_to_drag">Stuknij dwukrotnie, aby przeciągnąć</string>
|
||||
<string name="hold_to_drag">Przytrzymaj, aby przeciągnąć</string>
|
||||
<string name="about_kde_about">"<h1>O programie</h1> <p>KDE to światowa społeczność inżynierów oprogramowania, artystów, pisarzy, tłumaczy i twórców, którzy są częścią rozwoju <a href=https://www.gnu.org/philosophy/free-sw.html>Wolnego Oprogramowania</a>. KDE tworzy środowisko pulpitu Plazmy, setki aplikacji i wiele bibliotek programistycznych, aby je wspierać.</p> <p>KDE jest przedsięwzięciem istniejącym ze współpracy; jego ruchami, czy produktami, nie steruje żaden pojedynczy byt. Pracujemy razem, aby osiągnąć wspólny cel, czyli budowę najlepszego Wolnego Oprogramowania na świecie. Każdy jest mile wiedziany, żeby <a href=https://community.kde.org/Get_Involved>dołączył i zaczął współtworzyć</a> KDE, włączając w to ciebie.</p> Odwiedź < href=https://www.kde.org/>https://www.kde.org/</a> po więcej szczegółów nt. społeczności KDE i oprogramowania, które tworzymy."</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Zgłaszaj błędy lub życzenia</h1> <p>Oprogramowanie zawsze można ulepszyć, a zespół KDE jest gotowy, aby to robić. Jednakże ty - użytkownik - musisz nam powiedzieć o tym, co nie działa jak powinno lub co można zrobić lepiej.</p> <p>KDE ma system obsługi błędów. Odwiedź <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> lub użyj przycisku \"Zgłoś błąd\" z ekranu o programie do zgłaszania błędów.</p> Jeśli masz sugestie nt. usprawnień, to także możesz ją zarejestrować w naszym systemie obsługi błędów. Upewnij się, że użyjesz ważności o nazwie \"Lista życzeń\".</string>
|
||||
<string name="about_kde_join_kde"><h1>Dołącz do KDE</h1> <p>Nie musisz być programistą, aby być członkiem zespołu KDE. Możesz dołączyć do zespołów językowych, które tłumaczą oprogramowanie. Możesz dostarczać grafikę, zestawy wyglądu, dźwięki i ulepszać dokumentację. To ty decydujesz!</p> <p>odwiedź <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> po szczegóły nt. projektów, w których możesz wziąć udział .</p> Jeśli potrzebujesz więcej szczegółów lub dokumentacji, to odwiedziny na <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> dadzą ci to, czego chcesz.</string>
|
||||
@@ -419,11 +421,13 @@
|
||||
<string name="maxim_leshchenko_task">Usprawnienia do interfejsu i strony o programie</string>
|
||||
<string name="holger_kaelberer_task">Wtyczka zdalnej klawiatury i usuwanie błędów</string>
|
||||
<string name="saikrishna_arcot_task">Wsparcie do obsługi klawiatury we wtyczce zdalnego wprowadzania, usuwanie błędów i ogólne usprawnienia</string>
|
||||
<string name="shellwen_chen_task">Polepszenie bezpieczeństwa SFTP, polepszenie łatwości w utrzymaniu tego projektu, poprawki błędów oraz ogólne ulepszenia</string>
|
||||
<string name="everyone_else">Inni którzy współtworzyli KDE Connect na przestrzeni lat</string>
|
||||
<string name="send_clipboard">Wysyłanie schowka</string>
|
||||
<string name="tap_to_execute">Stuknij, aby wykonać</string>
|
||||
<string name="plugin_stats">Statystyki wtyczek</string>
|
||||
<string name="enable_udp_broadcast">Włącz wykrywanie urządzeń UDP</string>
|
||||
<string name="enable_bluetooth">Włącz Bluetooth (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Należy zezwolić na powiadomienia, aby móc je otrzymywać z innych urządzeń</string>
|
||||
<string name="findmyphone_notifications_explanation">Uprawnienia powiadomień są potrzebne, aby telefon mógł dzwonić, gdy aplikacja działa w tle</string>
|
||||
<string name="no_notifications">Powiadomienia są wyłączone, więc nie otrzymasz żadnych próśb o sparowanie.</string>
|
||||
|
@@ -18,6 +18,7 @@
|
||||
<string name="pref_plugin_clipboard_sent">Área de transferência enviada</string>
|
||||
<string name="pref_plugin_mousepad">Introdução de dados remota</string>
|
||||
<string name="pref_plugin_mousepad_desc">Use seu celular ou tablet como mouse e teclado</string>
|
||||
<string name="pref_plugin_presenter">Controle remoto para apresentações</string>
|
||||
<string name="pref_plugin_presenter_desc">Use o seu dispositivo para mudar os slides de uma apresentação</string>
|
||||
<string name="pref_plugin_remotekeyboard">Receber pressionamento de teclas remoto</string>
|
||||
<string name="pref_plugin_remotekeyboard_desc">Recebe os eventos de pressionamento de teclas dos dispositivos remotos</string>
|
||||
@@ -51,6 +52,7 @@
|
||||
<string name="remotekeyboard_multiple_connections">Existe mais que uma conexão a teclados remotos. Selecione o dispositivo a configurar</string>
|
||||
<string name="open_mousepad">Introdução de dados remota</string>
|
||||
<string name="mousepad_info">Mova um dedo pela tela para mover o ponteiro do mouse. Dê um toque para clicar e use dois/três dedos para os botões da direita e do meio. Use dois dedos para rolar a tela. Use uma pressão longa para arrastar e soltar. A funcionalidade do mouse giroscópio pode ser habilitada nas preferências do plugin.</string>
|
||||
<string name="mousepad_info_no_gestures">Mova o dedo na tela para mover o cursor do mouse, toque para um clique.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Entrada de teclado não suportada pelo dispositivo emparelhado</string>
|
||||
<string name="mousepad_single_tap_settings_title">Definir ação do toque com um dedo</string>
|
||||
<string name="mousepad_double_tap_settings_title">Definir ação do toque com dois dedos</string>
|
||||
@@ -59,6 +61,7 @@
|
||||
<string name="mousepad_mouse_buttons_title">Mostrar botões do mouse</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Definir aceleração do ponteiro</string>
|
||||
<string name="mousepad_scroll_direction_title">Direção de rolagem inversa</string>
|
||||
<string name="mousepad_scroll_sensitivity_title">Sensibilidade da rolagem</string>
|
||||
<string name="gyro_mouse_enabled_title">Ativar mouse giroscópio</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Sensibilidade do giroscópio</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
@@ -118,6 +121,8 @@
|
||||
<string name="my_device_fingerprint">A impressão digital SHA256 do certificado do seu dispositivo é:</string>
|
||||
<string name="remote_device_fingerprint">A impressão digital SHA256 do certificado do dispositivo remoto é:</string>
|
||||
<string name="pair_requested">Solicitação de emparelhamento</string>
|
||||
<string name="pair_succeeded">Emparelhado com sucesso</string>
|
||||
<string name="pairing_request_from">Emparelhamento solicitado por %1s</string>
|
||||
<plurals name="incoming_file_title">
|
||||
<item quantity="one">Recebendo %1$d arquivo de %2$s</item>
|
||||
<item quantity="other">Recebendo %1$d arquivos de %2$s</item>
|
||||
@@ -154,6 +159,7 @@
|
||||
<string name="received_file_text">Toque para abrir o \'%1s\'</string>
|
||||
<string name="cannot_create_file">Não foi possível criar o arquivo %s</string>
|
||||
<string name="tap_to_answer">Toque para responder</string>
|
||||
<string name="left_click">Enviar um clique de botão esquerdo</string>
|
||||
<string name="right_click">Enviar um Botão Direito</string>
|
||||
<string name="middle_click">Enviar um Botão do Meio</string>
|
||||
<string name="show_keyboard">Mostrar teclado</string>
|
||||
@@ -180,8 +186,12 @@
|
||||
<item>1 minuto</item>
|
||||
<item>2 minutos</item>
|
||||
</string-array>
|
||||
<string name="mpris_notifications_explanation">A permissão de notificações é necessária para exibir a mídia remotas na gaveta de notificações</string>
|
||||
<string name="mpris_notification_settings_title">Mostrar a notificação do controle multimídia</string>
|
||||
<string name="mpris_notification_settings_summary">Permite controlar os seus reprodutores de mídias sem abrir o KDE Connect</string>
|
||||
<string name="share_to">Compartilhar com...</string>
|
||||
<string name="unreachable_device">%s (Inalcançável)</string>
|
||||
<string name="unreachable_device_url_share_text">URLs compartilhadas com um dispositivo inalcançável serão enviadas para ele assim que ele voltar a ficar disponível.\n\n</string>
|
||||
<string name="protocol_version_newer">Este dispositivo usa uma versão mais recente do protocolo</string>
|
||||
<string name="plugin_settings_with_name">Configurações de %s</string>
|
||||
<string name="invalid_device_name">Nome do dispositivo inválido</string>
|
||||
@@ -248,10 +258,12 @@
|
||||
<string name="optional_permission_explanation">Você precisa conceder permissões extras para ativar todas as funções</string>
|
||||
<string name="plugins_need_optional_permission">Alguns plugins possuem recursos desativados devido à falta de permissões (toque para obter mais informações):</string>
|
||||
<string name="share_optional_permission_explanation">Para receber arquivos você precisa permitir o acesso ao armazenamento</string>
|
||||
<string name="share_notifications_explanation">Para acompanhar o progresso ao enviar e receber arquivos, você precisa permitir as notificações</string>
|
||||
<string name="telepathy_permission_explanation">Para ler e gravar SMS a partir do seu ambiente de trabalho é necessário conceder permissão para SMS</string>
|
||||
<string name="telephony_permission_explanation">Para ver as chamadas telefônicas no seu ambiente de trabalho é preciso dar permissões para registro de chamadas telefônicas e do estado do celular</string>
|
||||
<string name="telephony_optional_permission_explanation">Para ver o nome de um contato em vez do seu número de telefone é necessário conceder acesso aos contatos do celular</string>
|
||||
<string name="contacts_permission_explanation">Para compartilhar o seu livro de endereços com o ambiente de trabalho é necessário conceder permissão para os contatos</string>
|
||||
<string name="contacts_per_device_confirmation">Os contatos do seu telefone serão copiados para este dispositivo, dessa forma eles poderão ser utilizados pelo aplicativo de SMS do KDE Connect e por outros.</string>
|
||||
<string name="select_ringtone">Selecionar um toque de chamada</string>
|
||||
<string name="telephony_pref_blocked_title">Números bloqueados</string>
|
||||
<string name="telephony_pref_blocked_dialog_desc">Não mostrar as chamadas e SMS destes números. Indique um número por linha.</string>
|
||||
@@ -269,6 +281,9 @@
|
||||
<string name="notification_channel_default">Outras notificações</string>
|
||||
<string name="notification_channel_persistent">Indicador persistente</string>
|
||||
<string name="notification_channel_media_control">Controle multimídia</string>
|
||||
<string name="notification_channel_filetransfer">Chegada de arquivo via transferência</string>
|
||||
<string name="notification_channel_filetransfer_upload">Saída de arquivo via transferência</string>
|
||||
<string name="notification_channel_filetransfer_error">Erro na transferência de arquivo</string>
|
||||
<string name="notification_channel_high_priority">Prioridade alta</string>
|
||||
<string name="mpris_stop">Parar o reprodutor atual</string>
|
||||
<string name="copy_url_to_clipboard">Copiar URL para a área de transferência</string>
|
||||
@@ -277,6 +292,10 @@
|
||||
<string name="runcommand_notpaired">Dispositivo não emparelhado</string>
|
||||
<string name="runcommand_nosuchdevice">Este dispositivo não existe</string>
|
||||
<string name="runcommand_noruncommandplugin">Este dispositivo não tem o plugin \'Executar comando\' ativo</string>
|
||||
<string name="runcommand_category_device_controls_title">Controles do dispositivo</string>
|
||||
<string name="runcommand_device_controls_summary">Caso o seu dispositivo suporte \"controles de dispositivo\", os comandos que você configurou aparecerão aqui.</string>
|
||||
<string name="set_runcommand_name_as_title">set_runcommand_name_as_title</string>
|
||||
<string name="runcommand_name_as_title_title">Exibir o nome como título</string>
|
||||
<string name="pref_plugin_findremotedevice">Procurar dispositivo remoto</string>
|
||||
<string name="pref_plugin_findremotedevice_desc">Fazer tocar o seu dispositivo remoto</string>
|
||||
<string name="ring">Toque</string>
|
||||
@@ -359,7 +378,7 @@
|
||||
<string name="email_contributor">E-mail do colaborador\n%s</string>
|
||||
<string name="visit_contributors_homepage">Visite o site do colaborador\n%s</string>
|
||||
<string name="version">Versão %s</string>
|
||||
<string name="about_kde">Sobre o KDE</string>
|
||||
<string name="about_kde">Sobre a KDE</string>
|
||||
<string name="kde_be_free">KDE - Seja livre!</string>
|
||||
<string name="kde">KDE</string>
|
||||
<string name="konqi">Konqi</string>
|
||||
@@ -370,9 +389,12 @@
|
||||
<string name="send_compose">Enviar</string>
|
||||
<string name="compose_send_title">Enviar composto</string>
|
||||
<string name="open_compose_send">Compor texto</string>
|
||||
<string name="about_kde_about">"<h1>About</h1> <p>O KDE é uma comunidade mundial de engenheiros de software, artistas, escritores, tradutores e criadores comprometidos com o desenvolvimento de <a href=https://www.gnu.org/philosophy/free-sw.html>Software Livre.</a> O KDE produz o ambiente de desktop Plasma, centenas de aplicativos e muitas bibliotecas de software que os suportam.</p> <p>KDE é uma empresa cooperativa: nenhuma entidade controla sua direção ou produtos. Em vez disso, trabalhamos juntos para alcançar o objetivo comum de construir o melhor software livre do mundo. Todos são bem-vindos para <a href=https://community.kde.org/Get_Involved>participar e contribuir com o KDE</a> , inclusive você.</p> Visite <a href=https://www.kde.org/>https://www.kde.org/</a> para obter mais informações sobre a comunidade KDE e o software que produzimos."</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Reportar bugs ou sugestões</h1> <p> Software sempre pode ser melhorado e a equipe do KDE está pronta para isso. No entanto, você - o usuário - deve nos informar quando algo não funcionar conforme o esperado ou puder ser feito melhor.</p> <p>KDE possui um sistema de rastreamento de bugs. Visite <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> ou use o botão \"Relatar erro\" na tela \"sobre\" para relatar bugs.</p> Se você tiver uma sugestão de melhoria, poderá usar o sistema de rastreamento de bugs para registrar sua solicitação. Certifique-se de usar a opção \"wishlist\" no campo \"severity\".</string>
|
||||
<string name="about_kde_support_kde"><h1>Apoie o KDE</h1> <p>O software do KDE está e sempre estará disponível gratuitamente, mas criá-lo não é gratuito.</p> <p>Para apoiar o desenvolvimento, a comunidade KDE formou o KDE e.V., uma organização sem fins lucrativos fundada legalmente na Alemanha. KDE e.V. representa a comunidade KDE em questões legais e financeiras. Veja <a href=https://ev.kde.org/>https://ev.kde.org/</a> para obter informações sobre o KDE e.V.</p> <p>O KDE se beneficia de muitos tipos de contribuições, inclusive financeiras. Usamos os fundos para reembolsar os membros e outros pelas despesas em que incorrem ao contribuir. Outros fundos são usados para apoio jurídico e organização de conferências e reuniões.</p> <p>Gostaríamos de incentivá-lo a apoiar nossos esforços com uma doação financeira, usando uma das formas descritas em <a href=https://www.kde.org/community/donations/>https://www.kde.org/community/donations/</a>.</p> Muito obrigado antecipadamente por seu apoio.</string>
|
||||
<string name="double_tap_to_drag">Toque duplo para arrastar</string>
|
||||
<string name="hold_to_drag">Segurar para arrastar</string>
|
||||
<string name="about_kde_about"><h1>About</h1> <p>A KDE é uma comunidade mundial de engenheiros de software, artistas, escritores, tradutores e criadores comprometidos com o desenvolvimento de <a href=https://www.gnu.org/philosophy/free-sw.html>Software Livre.</a> A KDE produz o ambiente de desktop Plasma, centenas de aplicativos e muitas bibliotecas de software que os suportam.</p> <p>A KDE é uma empresa cooperativa: nenhuma entidade controla sua direção ou produtos. Em vez disso, trabalhamos juntos para alcançar o objetivo comum de construir o melhor software livre do mundo. Todos são bem-vindos para <a href=https://community.kde.org/Get_Involved>participar e contribuir com a KDE</a> , inclusive você.</p> Visite <a href=https://www.kde.org/>https://www.kde.org/</a> para obter mais informações sobre a comunidade KDE e o software que produzimos.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Reportar bugs ou sugestões</h1> <p> Software sempre pode ser melhorado e a equipe do KDE está pronta para isso. No entanto, você - o usuário - deve nos informar quando algo não funcionar conforme o esperado ou puder ser feito melhor.</p> <p>A KDE possui um sistema de rastreamento de bugs. Visite <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> ou use o botão \"Relatar erro\" na tela \"sobre\" para relatar bugs.</p> Se você tiver uma sugestão de melhoria, poderá usar o sistema de rastreamento de bugs para registrar sua solicitação. Certifique-se de usar a opção \"wishlist\" no campo \"severity\".</string>
|
||||
<string name="about_kde_join_kde"><h1>Junte-se à KDE</h1> <p> Você não precisa ser um desenvolvedor de software para ser membro da equipe KDE. Você pode se juntar às equipes de idioma que traduzem interfaces de programas. Você pode fornecer imagens, temas, sons e documentação aprimorada. Você decide!</p> <p>Visite <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> para obter informações sobre alguns projetos dos quais você pode participar.</p> Caso você precise de mais informações ou documentação, visite <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> talvez você encontre o que procura.</string>
|
||||
<string name="about_kde_support_kde"><h1>Apoie a KDE</h1> <p>O software da KDE está e sempre estará disponível gratuitamente, mas criá-lo não é gratuito.</p> <p>Para apoiar o desenvolvimento, a comunidade KDE formou a KDE e.V., uma organização sem fins lucrativos fundada legalmente na Alemanha. KDE e.V. representa a comunidade KDE em questões legais e financeiras. Veja <a href=https://ev.kde.org/>https://ev.kde.org/</a> para obter informações sobre o KDE e.V.</p> <p>A KDE se beneficia de muitos tipos de contribuições, inclusive financeiras. Usamos os fundos para reembolsar os membros e outros pelas despesas em que incorrem ao contribuir. Outros fundos são usados para apoio jurídico e organização de conferências e reuniões.</p> <p>Gostaríamos de incentivar você a apoiar nossos esforços com uma doação financeira, usando uma das formas descritas em <a href=https://www.kde.org/community/donations/>https://www.kde.org/community/donations/</a>.</p> Agradecemos, antecipadamente, o seu apoio.</string>
|
||||
<string name="maintainer_and_developer">Mantenedor e desenvolvedor</string>
|
||||
<string name="developer">Desenvolvedor</string>
|
||||
<string name="apple_support">Suporte ao macOS. Trabalhando no suporte ao iOS</string>
|
||||
@@ -383,8 +405,17 @@
|
||||
<string name="maxim_leshchenko_task">Melhorias na interface e nesta página Sobre</string>
|
||||
<string name="holger_kaelberer_task">Plugin de teclado remoto e correções de erros</string>
|
||||
<string name="saikrishna_arcot_task">Suporte para usar o teclado no plugin de entrada remoto, correções de erros e melhorias em geral</string>
|
||||
<string name="everyone_else">Todos os outros que contribuíram para o KDE Connect ao longo dos anos</string>
|
||||
<string name="shellwen_chen_task">Melhorar a segurança do SFTP, melhorar a capacidade de manutenção deste projeto, correções de erros e melhorias em geral</string>
|
||||
<string name="everyone_else">Todo mundo que contribuiu para o KDE Connect ao longo dos anos</string>
|
||||
<string name="send_clipboard">Enviar para área de transferência</string>
|
||||
<string name="tap_to_execute">Toque para executar</string>
|
||||
<string name="plugin_stats">Estatísticas do plugin</string>
|
||||
<string name="enable_udp_broadcast">Habilitar a descoberta de dispositivo via UDP</string>
|
||||
<string name="receive_notifications_permission_explanation">Notificações devem ser permitidas para receber notificações dos outros dispositivos</string>
|
||||
<string name="findmyphone_notifications_explanation">A permissão de notificação é necessária para que o telefone possa tocar quando o app estiver em segudo plano</string>
|
||||
<string name="no_notifications">As notificações estão desabilitadas, você não receberá notificações de solicitação de pareamento.</string>
|
||||
<string name="mpris_keepwatching">Continuar a execução</string>
|
||||
<string name="mpris_keepwatching_settings_title">Continuar a execução</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Exibe uma notificação silenciosa para continuar a execução neste dispositivo após o fechamento da mídia.</string>
|
||||
<string name="notification_channel_keepwatching">Continuar a execução</string>
|
||||
</resources>
|
||||
|
@@ -405,6 +405,8 @@
|
||||
<string name="send_compose">Pošlji</string>
|
||||
<string name="compose_send_title">Sestavite besedilo</string>
|
||||
<string name="open_compose_send">Sestavite besedilo</string>
|
||||
<string name="double_tap_to_drag">Dvakrat tapnite za povlek</string>
|
||||
<string name="hold_to_drag">Držite za povlek</string>
|
||||
<string name="about_kde_about"><h1>O programu</h1> <p>KDE je svetovna skupnost programskih inženirjev, umetnikov, piscev, prevajalcev in ustvarjalcev, ki so podporniki razvoja <a href=https://www.gnu.org/philosophy/free-sw.html>Prostega programja</a>. KDE izdeluje Namizno okolje Plasma, stotine aplikacij in veliko programskih knjižnic, ki jih podpirajo. </p> <p>KDE je zadružno podjetje: noben deležnik posamično ne obvladuje smeri razvoja ali izdelka. Namesto tega delamo skupaj, da bi dosegli skupni cilj razvoja, da bi prišli do najboljšega prostega programja na svetu. Vsakdo je dobrodošel v skupnost <a href=https://community.kde.org/Get_Involved>da se pridruži in prispeva </a> v KDE vključno z vami. </p> Obiščite <a href=https://www.kde.org/>https://www.kde.org/</a> za več informacij o skupnosti KDE community in programju, ki ga razvijamo.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Poročajte o napakah in željah</h1> <p>Programje je vedno mogoče izboljšati in skupina KDE je vedno pripravljena na izboljšave. Vendar, vi - kot uporabnica ali uporabnik - nam morate povedati, kadar nekaj ne deluje kot pričakujete ali bi bilo lahko izdelano bolje. </p> <p>KDE ima sistem sledenja napak. Obiščite <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> ali uporabite gumb \"Poročaj o napaki\" na zaslonu O programu za poročanje napak.</p> Če imate sugestijo za izboljšanje ste povabljeni, da uporabite sistem za sledenje napakam in zabeležite vašo željo. Zagotovite, da boste resnost napake označili kot \"Seznam želja - Wishlist\".</string>
|
||||
<string name="about_kde_join_kde"><h1>Pridružite se KDE</h1> <p>Ni treba, da ste razvijalci programov, da bi bili člani skupnosti KDE. Lahko se pridružite jezikovnim skupinam za prevajanje uporabniških vmesnikov. Lahko prispevate risbe, teme, zvoke in izboljšano dokumentacijo. Sami se odločite!</p> <p>Obiščite <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> za informacije o projektih, katerim lahko prispevate.</p> Če potrebujete več informacij ali dokumentacije, potem obiščite <a href=https://techbase.kde.org/>https://techbase.kde.org/</a>, kjer boste dobili potrebna navodila.</string>
|
||||
@@ -419,6 +421,7 @@
|
||||
<string name="maxim_leshchenko_task">Izboljšave uporabniškega vmesnika in ta stran o programu</string>
|
||||
<string name="holger_kaelberer_task">Vtičnik za oddaljeno tipkovnico in popravki napak</string>
|
||||
<string name="saikrishna_arcot_task">Podpora za uporabo tipkovnice vtičnika za oddaljen vnos, popravki napak in splošne izboljšave</string>
|
||||
<string name="shellwen_chen_task">Izboljšanje varnosti SFTP, izboljšanje vzdržljivosti tega projekta, popravki napak in splošne izboljšave</string>
|
||||
<string name="everyone_else">Vsi ostali, ki so prispevali za KDE Connect v letih razvoja</string>
|
||||
<string name="send_clipboard">Pošlji odložišče</string>
|
||||
<string name="tap_to_execute">Tapkajte za izvedbo</string>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Skicka</string>
|
||||
<string name="compose_send_title">Skriv och skicka</string>
|
||||
<string name="open_compose_send">Skriv text</string>
|
||||
<string name="double_tap_to_drag">Dubbeltryck för att dra</string>
|
||||
<string name="hold_to_drag">Håll för att dra</string>
|
||||
<string name="about_kde_about"><h1>Om</h1> <p>KDE är ett världsomspännande nätverk av programvaruingenjörer, grafiker, författare, översättare och kreatörer som är engagerade i utveckling av <a href=https://www.gnu.org/philosophy/free-sw.html>fri programvara</a>. KDE producerar skrivbordsmiljön Plasma, hundratals program, och de talrika programvarubibliotek som stöder dem.</p> <p>KDE är ett kooperativ, där ingen enskild person styr inriktningen eller produkterna. Istället arbetar vi tillsammans för att uppnå det gemensamma målet att skapa värdens finaste fria programvara. Alla är välkomna att <a href=https://community.kde.org/Get_Involved>gå med och bidra</a> till KDE, inklusive du själv.</p> Besök <a href=https://www.kde.org/>https://www.kde.org/</a> för ytterligare information om KDE-gemenskapen och programvaran vi skapar.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Rapportera fel eller önskemål</h1> <p>Programvara kan alltid förbättras, och KDE-gruppen är beredd att göra det. Men du - användaren - måste berätta för oss om något inte fungerar som förväntat eller kunde ha gjorts bättre.</p> <p>KDE har ett felrapporteringssystem. Besök <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> eller använd dialogrutan \"Rapportera fel...\" från om-skärmen för att rapportera fel.</p> Om du har ett förslag på en förbättring kan du använda felrapporteringssystemet för att registrera din önskan. Se då till att du använder allvarlighetsgraden \"Önskan\".</string>
|
||||
<string name="about_kde_join_kde"><h1>Gå med i KDE</h1> <p>Du behöver inte vara en programutvecklare för att bli medlem i KDE-gruppen. Du kan gå med i de nationella grupperna som översätter programmens gränssnitt. Du kan bidra med grafik, teman, ljud och förbättrad dokumentation. Det är upp till dig!</p> <p>Besök <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> för information om några projekt som du kan delta i.</p>Om du behöver mer information eller dokumentation kommer ett besök på <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> att ge dig det du behöver.</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Förbättringar av användargränssnitt och den här om-sidan</string>
|
||||
<string name="holger_kaelberer_task">Insticksprogram för fjärrtangentbord och felrättningar</string>
|
||||
<string name="saikrishna_arcot_task">Stöd för användning av tangentbordet i insticksprogrammet för fjärrinmatning, felrättningar och allmänna förbättringar</string>
|
||||
<string name="shellwen_chen_task">Förbättra säkerheten för SFTP, förbättra projektets underhållbarhet, felrättningar och allmänna förbättringar</string>
|
||||
<string name="everyone_else">Alla andra som har bidragit till KDE-anslut under alla år</string>
|
||||
<string name="send_clipboard">Skicka klippbord</string>
|
||||
<string name="tap_to_execute">Rör för att köra</string>
|
||||
<string name="plugin_stats">Insticksprogramstatistik</string>
|
||||
<string name="enable_udp_broadcast">Aktivera UDP-enhetsupptäckt</string>
|
||||
<string name="enable_bluetooth">Aktivera Blåtand (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Underrättelser måste vara tillåtna för att kunna ta emot dem från andra apparater</string>
|
||||
<string name="findmyphone_notifications_explanation">Underrättelserättigheter krävs så att telefonen kan ringa när programmet är i bakgrunden</string>
|
||||
<string name="no_notifications">Underrättelser är inaktiverade, så en underrättelse om inkommande begäran att para ihop inte tas emot.</string>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Gönder</string>
|
||||
<string name="compose_send_title">Gönderi oluştur</string>
|
||||
<string name="open_compose_send">Metin oluştur</string>
|
||||
<string name="double_tap_to_drag">Sürüklemek için çift dokunun</string>
|
||||
<string name="hold_to_drag">Sürüklemek için tutun</string>
|
||||
<string name="about_kde_about"><h1>Hakkında</h1> <p>KDE, <a href=https://www.gnu.org/philosophy/free-sw.html>Özgür Yazılım</a> hareketine destek veren yazılım mühendislerinin, sanatçıların, yazarların, çevirmenlerin ve yaratıcıların bir araya geldiği dünya çapında bir topluluktur KDE, Plasma masaüstü ortamını, yüzlerce uygulamayı ve onları destekleyen sayısız yazılım kitaplığını üretir.</p> <p>KDE, işbirliğine dayalı bir kuruluştur: yönünü veya ürünlerini tek başına denetleyen bir kuruluş yoktur. Bunun yerine, dünyanın en iyi Özgür Yazılımını oluşturma ortak hedefine ulaşmak için birlikte çalışıyoruz. Siz de dahil olmak üzere herkes <a href=https://community.kde.org/Get_Involved>katılabilir</a> ve katkıda bulunabilir.</p> KDE topluluğu ve ürettiğimiz yazılımlar hakkında daha fazla bilgi için <a href=https://www.kde.org/>https://www.kde.org/</a> adresini ziyaret edin.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Hataları veya İsteklerinizi Bildirin</h1> <p>Yazılımlar her zaman iyileştirilebilir ve KDE takımın bunu yapmaya hazırdır. Ancak siz de bir şey beklendiği gibi gitmezse veya hata verirse bize bildirin.</p> <p>KDE’nin bir hata takip sistemi vardır. <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> adresini ziyaret edin veya hakkında ekranının ‘Hata Bildir’ düğmesini kullanarak hataları bildirin.</p> Bir iyileştirme için öneriniz varsa bunu bildirmek için hata takip sistemini kullanabilirsiniz; yalnızca “Wishlist” önceliğini kullandığınızdan emin olun.</string>
|
||||
<string name="about_kde_join_kde">"<h1>KDE’ye Katılın</h1> <p>KDE takımının bir üyesi olmak için yazılım geliştirici olmanıza gerek yoktur. Program arayüzlerini çeviren dil takımlarına katılabilirsiniz. Grafikler, temalar, sesler ve iyileştirilmiş belgelendirme sağlayabilirsiniz. Karar sizin!</p> <p>Katılabileceğiniz bazı projeler hakkında bilgi almak için <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> sayfasını ziyaret edin.</p> Daha fazla bilgiye veya belgeye gereksiniminiz varsa <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> sayfasında aradığınızı bulabilirsiniz."</string>
|
||||
@@ -403,11 +405,13 @@
|
||||
<string name="maxim_leshchenko_task">Kullanıcı arabirimi iyileştirmeleri ve bu hakkında sayfası</string>
|
||||
<string name="holger_kaelberer_task">Uzak klavye eklentisi ve hata düzeltmeleri</string>
|
||||
<string name="saikrishna_arcot_task">Uzaktan girdi eklentisinde klavye kullanımı desteği, hata düzeltmeleri ve genel iyileştirmeler</string>
|
||||
<string name="shellwen_chen_task">SFTP güvenliğini artır, projenin bakımını daha da kolaylaştır, hata düzeltmeleri ve genel iyileştirmeler</string>
|
||||
<string name="everyone_else">KDE Bağlan’a yıllar boyunca katkıda bulunan herkes</string>
|
||||
<string name="send_clipboard">Pano gönder</string>
|
||||
<string name="tap_to_execute">Yürütmek için dokun</string>
|
||||
<string name="plugin_stats">Eklenti istatistikleri</string>
|
||||
<string name="enable_udp_broadcast">UDP aygıt keşfini etkinleştir</string>
|
||||
<string name="send_clipboard">Pano Gönder</string>
|
||||
<string name="tap_to_execute">Yürütmek için Dokun</string>
|
||||
<string name="plugin_stats">Eklenti İstatistikleri</string>
|
||||
<string name="enable_udp_broadcast">UDP Aygıt Keşfini Etkinleştir</string>
|
||||
<string name="enable_bluetooth">Bluetooth’u Etkinleştir (Beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">Başka aygıtlardan gelen bildirimleri alabilmek için bildirimlere izin verilmesi gereklidir</string>
|
||||
<string name="findmyphone_notifications_explanation">Uygulama arka plandayken telefonun çalabilmesi için bildirim izni gereklidir</string>
|
||||
<string name="no_notifications">Bildirimler devre dışı; herhangi bir gelen eş bildirimi almayacaksınız.</string>
|
||||
|
@@ -405,6 +405,8 @@
|
||||
<string name="send_compose">Надіслати</string>
|
||||
<string name="compose_send_title">Надсилання редагованого</string>
|
||||
<string name="open_compose_send">Редагувати текст</string>
|
||||
<string name="double_tap_to_drag">Подвійне торкання для перетягування</string>
|
||||
<string name="hold_to_drag">Утримування для перетягування</string>
|
||||
<string name="about_kde_about"><h1>Інформація</h1> <p>KDE — це всесвітня спільнота програмістів, художників, авторів текстів, перекладачів та фахівців з полегшення користування програмами, які роблять свій внесок до розвитку <a href=https://www.gnu.org/philosophy/free-sw.html>вільного програмного забезпечення</a>. KDE створено стільничне середовище Плазма, сотні вільних програм і багато бібліотек, які є їхньою основою.</p> <p>Розробка KDE є спільною працею, у якій жоден з учасників не має переважного контролю над зусиллями або результатами роботи інших розробників KDE. Ми працюємо разом заради спільної мети — створення найкращого вільного програмного забезпечення. Кожен може <a href=https://community.kde.org/Get_Involved>долучитися і зробити свій внесок</a>, зокрема це можете зробити ви.</p> Відвідайте сайт <a href=https://www.kde.org/>https://www.kde.org/</a>, щоб дізнатися більше про спільноту KDE та створене нею програмне забезпечення.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Повідомляйте про вади і ваші побажання</h1> <p>Програмне забезпечення завжди потребує вдосконалення, і команда KDE готова це робити. Проте, вам (користувачеві) варто повідомити нам, якщо щось не працює, як слід, або щось можна покращити.</p> <p>KDE має систему стеження за вадами. Завітайте на сторінку <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> , щоб повідомити розробників про ваду у програмі.</p>Якщо у вас є пропозиція щодо вдосконалення, за допомогою цієї системи можна зареєструвати ваше побажання. Переконайтеся, що поле «Важливість» встановлено у значення «Список побажань» («Wishlist»).</string>
|
||||
<string name="about_kde_join_kde"><h1>Долучайтеся до KDE</h1> <p>Не обов\'язково бути програмістом, щоб належати до Команди KDE. Можете приєднатися до команд, що перекладають інтерфейс програм. Можете забезпечувати користувачів графікою, темами, звуками та вдосконаленою документацію. Вам вирішувати!</p> <p>Завітайте на сторінку <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> щодо інформації про деякі проєкти, у яких можна взяти участь.</p>Якщо ви потребуєте більше інформації або документації, відвідайте <a href=https://techbase.kde.org/>https://techbase.kde.org/</a>, щоб ознайомитися з нею.</string>
|
||||
@@ -419,11 +421,13 @@
|
||||
<string name="maxim_leshchenko_task">Удосконалення інтерфейсу та ця інформаційна сторінка</string>
|
||||
<string name="holger_kaelberer_task">Додаток бездротової клавіатури та виправлення вад</string>
|
||||
<string name="saikrishna_arcot_task">Підтримка використання клавіатури у додатку віддаленого введення, виправлення вад і загальні удосконалення</string>
|
||||
<string name="shellwen_chen_task">Удосконалення захисту SFTP, удосконалення можливості супроводу проєкту, виправлення вад і загальні удосконалення</string>
|
||||
<string name="everyone_else">Усім іншим, хто робив внесок до KDE Connect протягом років розробки</string>
|
||||
<string name="send_clipboard">Надіслати вміст буфера</string>
|
||||
<string name="tap_to_execute">Торкніться, щоб виконати</string>
|
||||
<string name="plugin_stats">Статистика щодо додатків</string>
|
||||
<string name="enable_udp_broadcast">Увімкнути виявлення пристроїв UDP</string>
|
||||
<string name="enable_bluetooth">Увімкнути Bluetooth (тест)</string>
|
||||
<string name="receive_notifications_permission_explanation">Для отримання сповіщень з інших пристроїв має бути уможливлено показ сповіщень</string>
|
||||
<string name="findmyphone_notifications_explanation">Для уможливлення дзвінків, коли програма перебуває у фоновому режимі, потрібні права доступу до сповіщень</string>
|
||||
<string name="no_notifications">Показ сповіщень вимкнено, ви не отримуватимете сповіщень щодо вхідних запитів на пов\'язування пристроїв.</string>
|
||||
|
@@ -381,6 +381,8 @@
|
||||
<string name="send_compose">发送</string>
|
||||
<string name="compose_send_title">编写发送</string>
|
||||
<string name="open_compose_send">编写文本</string>
|
||||
<string name="double_tap_to_drag">双击进行拖动</string>
|
||||
<string name="hold_to_drag">按住进行拖动</string>
|
||||
<string name="about_kde_about"><h1>关于</h1> <p>KDE 是由一群致力于<a href=https://www.gnu.org/philosophy/free-sw.html>自由软件</a>事业的人们所组成的全球性协作社区。它的成员包括了来自世界各地的软件工程师、艺术工作者、文字工作者、翻译人员和其他创意人员。KDE 社区开发了 Plasma 桌面环境、数百款功能各异的应用软件、以及用于支持它们的大量程序库。</p> <p>KDE 是一项立足于协作精神的事业,它的运作和产出不受任何单一个人或者机构的控制。我们的共同目标是为全世界带来高品质的自由软件。不管您来自何方,我们都欢迎您<a href=https://community.kde.org/Get_Involved>加入 KDE 并做出贡献</a>。</p>请访问 <a href=https://www.kde.org/>https://www.kde.org/</a> 来了解 KDE 社区和软件的更多信息。</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>报告程序缺陷和需求</h1> <p>KDE 团队一直致力于改进软件的品质。为了做到这一点,倾听来自用户的反馈非常重要。如果您遇到了软件不能正常工作的情况,请务必告诉我们。如果您有关于改进软件的想法,也请与我们分享。</p> <p>KDE 建有程序缺陷跟踪系统,请访问 <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> 或者使用“帮助”菜单中的“报告缺陷”对话框填写报告。</p>如果您想要提出改进建议而不是报告程序缺陷,请确保在表格的 Severity (严重程度) 选单中选择“Wishlist (需求)”。</string>
|
||||
<string name="about_kde_join_kde"><h1>加入 KDE</h1> <p>即使您不是软件开发人员,我们也欢迎您加入 KDE 的队伍!您可以加入各种语言的翻译团队来翻译软件的界面,您还可以制作图像、主题、音效,或者改进软件配套的文档……您的角色您来定!</p> <p>请访问 <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> 来物色您感兴趣的项目。</p>如需了解更多相关信息和文档,请访问 <a href=https://techbase.kde.org/>https://techbase.kde.org/</a>。</string>
|
||||
@@ -395,6 +397,7 @@
|
||||
<string name="maxim_leshchenko_task">界面改进和此关于页面</string>
|
||||
<string name="holger_kaelberer_task">远程键盘插件和程序缺陷修正</string>
|
||||
<string name="saikrishna_arcot_task">支持在远程输入插件中使用键盘、程序缺陷修正和常规改进</string>
|
||||
<string name="shellwen_chen_task">改进 SFTP 的安全性,改进此项目的可维护性,修复程序缺陷和常规改进</string>
|
||||
<string name="everyone_else">以及多年来为 KDE Connect 作出过贡献的其他所有人</string>
|
||||
<string name="send_clipboard">发送剪贴板</string>
|
||||
<string name="tap_to_execute">轻触执行</string>
|
||||
|
@@ -374,11 +374,15 @@
|
||||
<string name="kde_be_free">KDE ─ 擁抱自由!</string>
|
||||
<string name="kde">KDE</string>
|
||||
<string name="konqi">Konqi</string>
|
||||
<string name="rise_up">往上</string>
|
||||
<string name="rise_down">往下</string>
|
||||
<string name="click_here_to_type">按這裡來打字</string>
|
||||
<string name="clear_compose">清除</string>
|
||||
<string name="send_compose">傳送</string>
|
||||
<string name="compose_send_title">撰寫傳送</string>
|
||||
<string name="open_compose_send">撰寫簡訊</string>
|
||||
<string name="double_tap_to_drag">雙擊來拖曳</string>
|
||||
<string name="hold_to_drag">按住來拖曳</string>
|
||||
<string name="about_kde_about"><h1>關於</h1><p>KDE 是一個由眾多軟體工程師、藝術作業者、文字作業者、翻譯人員和其他有志於<a href=https://www.gnu.org/philosophy/free-sw.html>自由軟體</a>事業的人們組成的世界性社群。KDE 做出了 Plasma 桌面環境、數百個應用程式和支援它們的軟體函式庫。</p><p>KDE 團隊不受任何單獨的團體、公司或機構控制。我們的目標是一同為製作世界上最好的自由軟體而努力。我們歡迎任何人<a href=https://community.kde.org/Get_Involved>加入並作出貢獻</a>——包含您。</p>請造訪 <a href=&quot;https://www.kde.org/&quot;>https://www.kde.org/</a> 來了解有關 KDE 社群與我們所做的軟體的更多資訊。</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>回報問題或希望功能</h1><p>軟體總是可以改善,而 KDE 團隊準備好繼續此項工作。不過您 — 作為使用者 — 需要在有東西沒有正常運作或是可以更好的時候告訴我們。</p><p>KDE 有一個問題追蹤系統。請造訪 <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> 或是從關於頁面使用「回報問題」按鈕來回報問題。</p>如果您有改善的建議,您也可以使用問題追蹤系統來寫下您的希望。此時請使用「Wishlist」(願望清單)作為問題的嚴重程度。</string>
|
||||
<string name="about_kde_join_kde"><h1>加入 KDE</h1><p>您不需要是軟體工程師也能加入 KDE 團隊。您可以加入翻譯團隊來翻譯程式的介面。您可以提供圖形、佈景主題、音效,或幫忙撰寫文件,看您喜歡做哪一項。</p><p>請參考 <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> 上的資訊看看您能幫上什麼忙。</p>如果您需要更多資訊或說明文件,請參照 <a href=https://techbase.kde.org/>https://techbase.kde.org/</a>,上面會有一些能幫忙您的資訊。</string>
|
||||
@@ -393,11 +397,13 @@
|
||||
<string name="maxim_leshchenko_task">使用者介面的改善,以及這個關於頁面</string>
|
||||
<string name="holger_kaelberer_task">遠端鍵盤外掛程式與問題修正</string>
|
||||
<string name="saikrishna_arcot_task">在遠端輸入外掛程式中對於鍵盤的支援、錯誤修正與一般改進</string>
|
||||
<string name="shellwen_chen_task">改善 SFTP 的安全性、改善本專案的易維護性、錯誤修正與一般改進</string>
|
||||
<string name="everyone_else">其他所有這些年來向 KDE 連線貢獻的人們</string>
|
||||
<string name="send_clipboard">傳送剪貼簿</string>
|
||||
<string name="tap_to_execute">按一下執行</string>
|
||||
<string name="plugin_stats">外掛程式統計</string>
|
||||
<string name="enable_udp_broadcast">啟用 UDP 裝置探索</string>
|
||||
<string name="enable_bluetooth">啟用藍牙 (beta)</string>
|
||||
<string name="receive_notifications_permission_explanation">需要允許通知才能接收來自其他裝置的通知</string>
|
||||
<string name="findmyphone_notifications_explanation">需要通知權限讓應用程式在背景時也能讓手機響鈴</string>
|
||||
<string name="no_notifications">通知已停用,您不會接收到送來的配對請求。</string>
|
||||
|
@@ -85,6 +85,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
<string name="mousepad_scroll_sensitivity_title">Scroll sensitivity</string>
|
||||
<string name="gyro_mouse_enabled" translatable="false">gyro_mouse_enabled</string>
|
||||
<string name="mousepad_mouse_buttons_enabled_pref" translatable="false">mouse_buttons_enabled</string>
|
||||
<string name="mousepad_doubletap_drag_enabled_pref" translatable="false">doubletap_drag_enabled</string>
|
||||
<string name="gyro_mouse_enabled_title">Enable gyroscope mouse</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Gyroscope sensitivity</string>
|
||||
<string name="gyro_mouse_sensitivity" translatable="false">gyro_mouse_sensitivity</string>
|
||||
@@ -513,6 +514,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
<string name="send_compose">Send</string>
|
||||
<string name="compose_send_title">Compose send</string>
|
||||
<string name="open_compose_send">Compose text</string>
|
||||
<string name="double_tap_to_drag">Double tap to drag</string>
|
||||
<string name="hold_to_drag">Hold to drag</string>
|
||||
|
||||
<string name="about_kde_about"><![CDATA[
|
||||
<h1>About</h1>
|
||||
@@ -555,6 +558,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
<string name="maxim_leshchenko_task">UI improvements and this about page</string>
|
||||
<string name="holger_kaelberer_task">Remote keyboard plugin and bug fixes</string>
|
||||
<string name="saikrishna_arcot_task">Support for using the keyboard in the remote input plugin, bug fixes and general improvements</string>
|
||||
<string name="shellwen_chen_task">Improve the security of SFTP, improve the maintainability of this project, bug fixes and general improvements</string>
|
||||
<string name="everyone_else">Everyone else who has contributed to KDE Connect over the years</string>
|
||||
|
||||
<string name="send_clipboard">Send clipboard</string>
|
||||
@@ -563,6 +567,7 @@ 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="enable_udp_broadcast">Enable UDP device discovery</string>
|
||||
<string name="enable_bluetooth">Enable bluetooth (beta)</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>
|
||||
|
@@ -90,6 +90,15 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:key="@string/mousepad_mouse_buttons_enabled_pref"
|
||||
android:title="@string/mousepad_mouse_buttons_title" />
|
||||
|
||||
<SwitchPreference
|
||||
android:id="@+id/mousepad_double_tap_drag_enabled_pref"
|
||||
android:defaultValue="true"
|
||||
android:key="@string/mousepad_doubletap_drag_enabled_pref"
|
||||
android:title="Drag and drop behavior"
|
||||
android:summaryOn="@string/double_tap_to_drag"
|
||||
android:summaryOff="@string/hold_to_drag"
|
||||
/>
|
||||
|
||||
|
||||
<org.kde.kdeconnect.Helpers.LongSummaryPreferenceCategory
|
||||
android:key="@string/sendkeystrokes_pref_category"
|
||||
|
7
res/xml/shortcuts.xml
Normal file
7
res/xml/shortcuts.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<share-target android:targetClass="org.kde.kdeconnect.Plugins.SharePlugin.ShareActivity">
|
||||
<data android:mimeType="*/*" />
|
||||
<category android:name="org.kde.kdeconnect.category.SHARE_TARGET" />
|
||||
</share-target>
|
||||
</shortcuts>
|
@@ -20,4 +20,15 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.ow2.asm:asm-util:9.6")
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "kdeconnect-android"
|
||||
|
@@ -255,6 +255,8 @@ class BluetoothLinkProvider(private val context: Context) : BaseLinkProvider() {
|
||||
context.unregisterReceiver(this)
|
||||
} catch (se: SecurityException) {
|
||||
Log.w("BluetoothLinkProvider", se)
|
||||
} catch (ia: IllegalArgumentException) {
|
||||
Log.w("BluetoothLinkProvider", ia) // Happens sometimes in unregisterReceiver
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -202,7 +202,7 @@ class ConnectionMultiplexer(socket: BluetoothSocket) : Closeable {
|
||||
socket!!.outputStream.write(data)
|
||||
}
|
||||
|
||||
private fun handleException(@Suppress("UNUSED_PARAMETER") ignored: IOException) {
|
||||
private fun handleException(@Suppress("UNUSED_PARAMETER") ignored: Exception) {
|
||||
lock.withLock {
|
||||
open = false
|
||||
for (channel in channels.values) {
|
||||
@@ -257,6 +257,8 @@ class ConnectionMultiplexer(socket: BluetoothSocket) : Closeable {
|
||||
socket!!.outputStream.write(data)
|
||||
} catch (e: IOException) {
|
||||
handleException(e)
|
||||
} catch (e: NullPointerException) {
|
||||
handleException(e)
|
||||
}
|
||||
channel.lockCondition.signalAll()
|
||||
}
|
||||
|
@@ -109,7 +109,14 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
|
||||
Log.i("KDE/LanLinkProvider", "identity packet received from a TCP connection from " + networkPacket.getString("deviceName"));
|
||||
identityPacketReceived(networkPacket, socket, LanLink.ConnectionStarted.Locally);
|
||||
|
||||
boolean deviceTrusted = isDeviceTrusted(networkPacket.getString("deviceId"));
|
||||
if (!deviceTrusted && !TrustedNetworkHelper.isTrustedNetwork(context)) {
|
||||
Log.i("KDE/LanLinkProvider", "Ignoring identity packet because the device is not trusted and I'm not on a trusted network.");
|
||||
return;
|
||||
}
|
||||
|
||||
identityPacketReceived(networkPacket, socket, LanLink.ConnectionStarted.Locally, deviceTrusted);
|
||||
}
|
||||
|
||||
//I've received their broadcast and should connect to their TCP socket and send my identity.
|
||||
@@ -149,6 +156,12 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
|
||||
Log.i("KDE/LanLinkProvider", "Broadcast identity packet received from " + identityPacket.getString("deviceName"));
|
||||
|
||||
boolean deviceTrusted = isDeviceTrusted(identityPacket.getString("deviceId"));
|
||||
if (!deviceTrusted && !TrustedNetworkHelper.isTrustedNetwork(context)) {
|
||||
Log.i("KDE/LanLinkProvider", "Ignoring identity packet because the device is not trusted and I'm not on a trusted network.");
|
||||
return;
|
||||
}
|
||||
|
||||
SocketFactory socketFactory = SocketFactory.getDefault();
|
||||
Socket socket = socketFactory.createSocket(address, tcpPort);
|
||||
configureSocket(socket);
|
||||
@@ -160,7 +173,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
out.write(myIdentity.serialize().getBytes());
|
||||
out.flush();
|
||||
|
||||
identityPacketReceived(identityPacket, socket, LanLink.ConnectionStarted.Remotely);
|
||||
identityPacketReceived(identityPacket, socket, LanLink.ConnectionStarted.Remotely, deviceTrusted);
|
||||
}
|
||||
|
||||
private void configureSocket(Socket socket) {
|
||||
@@ -171,6 +184,11 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDeviceTrusted(String deviceId) {
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
return preferences.getBoolean(deviceId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new 'identity' packet is received. Those are passed here by
|
||||
* {@link #tcpPacketReceived(Socket)} and {@link #udpPacketReceived(DatagramPacket)}.
|
||||
@@ -184,9 +202,10 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
* @param identityPacket identity of a remote device
|
||||
* @param socket a new Socket, which should be used to receive packets from the remote device
|
||||
* @param connectionStarted which side started this connection
|
||||
* @param deviceTrusted whether the packet comes from a trusted device
|
||||
*/
|
||||
@WorkerThread
|
||||
private void identityPacketReceived(final NetworkPacket identityPacket, final Socket socket, final LanLink.ConnectionStarted connectionStarted) throws IOException {
|
||||
private void identityPacketReceived(final NetworkPacket identityPacket, final Socket socket, final LanLink.ConnectionStarted connectionStarted, final boolean deviceTrusted) throws IOException {
|
||||
|
||||
if (!DeviceInfo.isValidIdentityPacket(identityPacket)) {
|
||||
Log.w("KDE/LanLinkProvider", "Invalid identity packet received.");
|
||||
@@ -203,10 +222,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
// If I'm the TCP server I will be the SSL client and viceversa.
|
||||
final boolean clientMode = (connectionStarted == LanLink.ConnectionStarted.Locally);
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
boolean isDeviceTrusted = preferences.getBoolean(deviceId, false);
|
||||
|
||||
if (isDeviceTrusted && !SslHelper.isCertificateStored(context, deviceId)) {
|
||||
if (deviceTrusted && !SslHelper.isCertificateStored(context, deviceId)) {
|
||||
//Device paired with and old version, we can't use it as we lack the certificate
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device == null) {
|
||||
@@ -214,13 +230,13 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
device.unpair();
|
||||
//Retry as unpaired
|
||||
identityPacketReceived(identityPacket, socket, connectionStarted);
|
||||
identityPacketReceived(identityPacket, socket, connectionStarted, deviceTrusted);
|
||||
}
|
||||
|
||||
String deviceName = identityPacket.getString("deviceName", "unknown");
|
||||
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + deviceName + " trusted:" + isDeviceTrusted);
|
||||
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + deviceName + " trusted:" + deviceTrusted);
|
||||
|
||||
final SSLSocket sslSocket = SslHelper.convertToSslSocket(context, socket, deviceId, isDeviceTrusted, clientMode);
|
||||
final SSLSocket sslSocket = SslHelper.convertToSslSocket(context, socket, deviceId, deviceTrusted, clientMode);
|
||||
sslSocket.addHandshakeCompletedListener(event -> {
|
||||
String mode = clientMode ? "client" : "server";
|
||||
try {
|
||||
|
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Backends.LanBackend;
|
||||
|
||||
import android.net.nsd.NsdManager;
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class NsdResolveQueue {
|
||||
|
||||
static final String LOG_TAG = "NsdResolveQueue";
|
||||
|
||||
final @NonNull NsdManager mNsdManager;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final LinkedList<PendingResolve> mResolveRequests = new LinkedList<>();
|
||||
|
||||
public NsdResolveQueue(NsdManager nsdManager) {
|
||||
this.mNsdManager = nsdManager;
|
||||
}
|
||||
|
||||
private static class PendingResolve {
|
||||
final @NonNull NsdServiceInfo serviceInfo;
|
||||
final @NonNull NsdManager.ResolveListener listener;
|
||||
|
||||
private PendingResolve(@NonNull NsdServiceInfo serviceInfo, @NonNull NsdManager.ResolveListener listener) {
|
||||
this.serviceInfo = serviceInfo;
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
public void resolveOrEnqueue(@NonNull NsdServiceInfo serviceInfo, @NonNull NsdManager.ResolveListener listener) {
|
||||
synchronized (mLock) {
|
||||
for (PendingResolve existing : mResolveRequests) {
|
||||
if (serviceInfo.getServiceName().equals(existing.serviceInfo.getServiceName())) {
|
||||
Log.i(LOG_TAG, "Not enqueuing a new resolve request for the same service: " + serviceInfo.getServiceName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
mResolveRequests.addLast(new PendingResolve(serviceInfo, new ListenerWrapper(listener)));
|
||||
|
||||
if (mResolveRequests.size() == 1) {
|
||||
resolveNextRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ListenerWrapper implements NsdManager.ResolveListener {
|
||||
private final @NonNull NsdManager.ResolveListener mListener;
|
||||
|
||||
private ListenerWrapper(@NonNull NsdManager.ResolveListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
mListener.onResolveFailed(serviceInfo, errorCode);
|
||||
|
||||
synchronized (mLock) {
|
||||
mResolveRequests.pop();
|
||||
resolveNextRequest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceResolved(NsdServiceInfo serviceInfo) {
|
||||
mListener.onServiceResolved(serviceInfo);
|
||||
|
||||
synchronized (mLock) {
|
||||
mResolveRequests.pop();
|
||||
resolveNextRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveNextRequest() {
|
||||
if (!mResolveRequests.isEmpty()) {
|
||||
PendingResolve request = mResolveRequests.getFirst();
|
||||
mNsdManager.resolveService(request.serviceInfo, request.listener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
package org.kde.kdeconnect.Backends.LanBackend
|
||||
|
||||
import android.net.nsd.NsdManager
|
||||
import android.net.nsd.NsdServiceInfo
|
||||
import android.util.Log
|
||||
import java.util.LinkedList
|
||||
|
||||
class NsdResolveQueue {
|
||||
val LOG_TAG: String = "NsdResolveQueue"
|
||||
|
||||
private val nsdManager: NsdManager
|
||||
private val lock: Any
|
||||
|
||||
private data class PendingResolve(val serviceInfo: NsdServiceInfo, val listener: NsdManager.ResolveListener)
|
||||
private val resolveRequests: LinkedList<PendingResolve>
|
||||
|
||||
constructor(nsdManager: NsdManager) {
|
||||
this.nsdManager = nsdManager
|
||||
this.lock = Any()
|
||||
this.resolveRequests = LinkedList<PendingResolve>()
|
||||
}
|
||||
|
||||
fun resolveOrEnqueue(serviceInfo: NsdServiceInfo, listener: NsdManager.ResolveListener) {
|
||||
synchronized(lock) {
|
||||
if (resolveRequests.any { r -> serviceInfo.serviceName == r.serviceInfo.serviceName }) {
|
||||
Log.i(LOG_TAG, "Not enqueuing a new resolve request for the same service: " + serviceInfo.serviceName)
|
||||
return
|
||||
}
|
||||
resolveRequests.addLast(PendingResolve(serviceInfo, ListenerWrapper(listener)))
|
||||
if (resolveRequests.size == 1) {
|
||||
resolveNextRequest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ListenerWrapper(private val listener: NsdManager.ResolveListener) : NsdManager.ResolveListener {
|
||||
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
|
||||
listener.onResolveFailed(serviceInfo, errorCode)
|
||||
postResolve()
|
||||
}
|
||||
|
||||
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
|
||||
listener.onServiceResolved(serviceInfo)
|
||||
postResolve()
|
||||
}
|
||||
|
||||
private fun postResolve() {
|
||||
synchronized(lock) {
|
||||
resolveRequests.pop()
|
||||
resolveNextRequest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveNextRequest() {
|
||||
if (resolveRequests.isNotEmpty()) {
|
||||
val request = resolveRequests.first
|
||||
nsdManager.resolveService(request.serviceInfo, request.listener)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Backends.LoopbackBackend;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.DeviceInfo;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
public class LoopbackLink extends BaseLink {
|
||||
|
||||
public LoopbackLink(Context context, BaseLinkProvider linkProvider) {
|
||||
super(context, linkProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LoopbackLink";
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@Override
|
||||
public boolean sendPacket(@NonNull NetworkPacket in, @NonNull Device.SendPacketStatusCallback callback, boolean sendPayloadFromSameThread) {
|
||||
packetReceived(in);
|
||||
if (in.hasPayload()) {
|
||||
callback.onPayloadProgressChanged(0);
|
||||
in.setPayload(in.getPayload());
|
||||
callback.onPayloadProgressChanged(100);
|
||||
}
|
||||
callback.onSuccess();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceInfo getDeviceInfo() {
|
||||
return DeviceHelper.getDeviceInfo(context);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Backends.LoopbackBackend
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.kde.kdeconnect.Backends.BaseLink
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider
|
||||
import org.kde.kdeconnect.Device
|
||||
import org.kde.kdeconnect.DeviceInfo
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper.getDeviceInfo
|
||||
import org.kde.kdeconnect.NetworkPacket
|
||||
|
||||
class LoopbackLink : BaseLink {
|
||||
constructor(context: Context, linkProvider: BaseLinkProvider) : super(context, linkProvider)
|
||||
|
||||
override fun getName(): String = "LoopbackLink"
|
||||
override fun getDeviceInfo(): DeviceInfo = getDeviceInfo(context)
|
||||
|
||||
@WorkerThread
|
||||
override fun sendPacket(packet: NetworkPacket, callback: Device.SendPacketStatusCallback, sendPayloadFromSameThread: Boolean): Boolean {
|
||||
packetReceived(packet)
|
||||
if (packet.hasPayload()) {
|
||||
callback.onPayloadProgressChanged(0)
|
||||
packet.payload = packet.payload // this triggers logic in the setter
|
||||
callback.onPayloadProgressChanged(100)
|
||||
}
|
||||
callback.onSuccess()
|
||||
return true
|
||||
}
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Backends.LoopbackBackend;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Network;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
|
||||
public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||
|
||||
private final Context context;
|
||||
|
||||
public LoopbackLinkProvider(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
onNetworkChange(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkChange(@Nullable Network network) {
|
||||
LoopbackLink link = new LoopbackLink(context, this);
|
||||
onConnectionReceived(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LoopbackLinkProvider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() { return 0; }
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Backends.LoopbackBackend
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Network
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider
|
||||
|
||||
class LoopbackLinkProvider : BaseLinkProvider {
|
||||
private val context: Context
|
||||
|
||||
constructor(context: Context) : super() {
|
||||
this.context = context
|
||||
}
|
||||
|
||||
override fun getName(): String = "LoopbackLinkProvider"
|
||||
override fun getPriority(): Int = 0
|
||||
|
||||
override fun onStart() {
|
||||
onNetworkChange(null)
|
||||
}
|
||||
|
||||
override fun onStop() { }
|
||||
|
||||
override fun onNetworkChange(network: Network?) {
|
||||
val link = LoopbackLink(context, this)
|
||||
onConnectionReceived(link)
|
||||
}
|
||||
}
|
@@ -1,304 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.SendFileActivity;
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/*
|
||||
* This class (still) does 3 things:
|
||||
* - Keeps the app running by creating a foreground notification.
|
||||
* - Holds references to the active LinkProviders, but doesn't handle the DeviceLink those create (the KdeConnect class does that).
|
||||
* - Listens for network connectivity changes and tells the LinkProviders to re-check for devices.
|
||||
* It can be started by the KdeConnectBroadcastReceiver on some events or when the MainActivity is launched.
|
||||
*/
|
||||
public class BackgroundService extends Service {
|
||||
private static final int FOREGROUND_NOTIFICATION_ID = 1;
|
||||
|
||||
private static BackgroundService instance;
|
||||
|
||||
private KdeConnect applicationInstance;
|
||||
|
||||
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<>();
|
||||
|
||||
public static BackgroundService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static boolean initialized = false;
|
||||
|
||||
// This indicates when connected over wifi/usb/bluetooth/(anything other than cellular)
|
||||
private final MutableLiveData<Boolean> connectedToNonCellularNetwork = new MutableLiveData<>();
|
||||
public LiveData<Boolean> isConnectedToNonCellularNetwork() {
|
||||
return connectedToNonCellularNetwork;
|
||||
}
|
||||
|
||||
public void updateForegroundNotification() {
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
//Update the foreground notification with the currently connected device list
|
||||
NotificationManager nm = ContextCompat.getSystemService(this, NotificationManager.class);
|
||||
nm.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
}
|
||||
}
|
||||
|
||||
private void registerLinkProviders() {
|
||||
linkProviders.add(new LanLinkProvider(this));
|
||||
// linkProviders.add(new LoopbackLinkProvider(this));
|
||||
linkProviders.add(new BluetoothLinkProvider(this));
|
||||
}
|
||||
|
||||
public void onNetworkChange(@Nullable Network network) {
|
||||
if (!initialized) {
|
||||
Log.d("KDE/BackgroundService", "ignoring onNetworkChange called before the service is initialized");
|
||||
return;
|
||||
}
|
||||
Log.d("KDE/BackgroundService", "onNetworkChange");
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onNetworkChange(network);
|
||||
}
|
||||
}
|
||||
|
||||
public void addConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.addConnectionReceiver(cr);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.removeConnectionReceiver(cr);
|
||||
}
|
||||
}
|
||||
|
||||
//This will called only once, even if we launch the service intent several times
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d("KdeConnect/BgService", "onCreate");
|
||||
instance = this;
|
||||
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("BackgroundService", this::updateForegroundNotification);
|
||||
|
||||
// Register screen on listener
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||
// See: https://developer.android.com/reference/android/net/ConnectivityManager.html#CONNECTIVITY_ACTION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
}
|
||||
registerReceiver(new KdeConnectBroadcastReceiver(), filter);
|
||||
|
||||
// Watch for changes on all network connections except cellular networks
|
||||
NetworkRequest.Builder networkRequestBuilder = getNonCellularNetworkRequestBuilder();
|
||||
ConnectivityManager cm = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
cm.registerNetworkCallback(networkRequestBuilder.build(), new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
Log.i("BackgroundService", "Valid network available");
|
||||
connectedToNonCellularNetwork.postValue(true);
|
||||
onNetworkChange(network);
|
||||
}
|
||||
@Override
|
||||
public void onLost(Network network) {
|
||||
Log.i("BackgroundService", "Valid network lost");
|
||||
connectedToNonCellularNetwork.postValue(false);
|
||||
}
|
||||
});
|
||||
|
||||
applicationInstance = KdeConnect.getInstance();
|
||||
|
||||
registerLinkProviders();
|
||||
addConnectionListener(applicationInstance.getConnectionListener()); // Link Providers need to be already registered
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStart();
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private static NetworkRequest.Builder getNonCellularNetworkRequestBuilder() {
|
||||
NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH);
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
||||
networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
|
||||
networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_USB)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN);
|
||||
}
|
||||
return networkRequestBuilder;
|
||||
}
|
||||
|
||||
public void changePersistentNotificationVisibility(boolean visible) {
|
||||
if (visible) {
|
||||
updateForegroundNotification();
|
||||
} else {
|
||||
Stop();
|
||||
Start(this);
|
||||
}
|
||||
}
|
||||
|
||||
private Notification createForegroundNotification() {
|
||||
|
||||
//Why is this needed: https://developer.android.com/guide/components/services#Foreground
|
||||
|
||||
ArrayList<String> connectedDevices = new ArrayList<>();
|
||||
ArrayList<String> connectedDeviceIds = new ArrayList<>();
|
||||
for (Device device : applicationInstance.getDevices().values()) {
|
||||
if (device.isReachable() && device.isPaired()) {
|
||||
connectedDeviceIds.add(device.getDeviceId());
|
||||
connectedDevices.add(device.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
if (connectedDeviceIds.size() == 1) {
|
||||
// Force open screen of the only connected device
|
||||
intent.putExtra(MainActivity.EXTRA_DEVICE_ID, connectedDeviceIds.get(0));
|
||||
}
|
||||
|
||||
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
NotificationCompat.Builder notification = new NotificationCompat.Builder(this, NotificationHelper.Channels.PERSISTENT);
|
||||
notification
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setOngoing(true)
|
||||
.setContentIntent(pi)
|
||||
.setPriority(NotificationCompat.PRIORITY_MIN) //MIN so it's not shown in the status bar before Oreo, on Oreo it will be bumped to LOW
|
||||
.setShowWhen(false)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
.setAutoCancel(false);
|
||||
notification.setGroup("BackgroundService");
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
//Pre-oreo, the notification will have an empty title line without this
|
||||
notification.setContentTitle(getString(R.string.kde_connect));
|
||||
}
|
||||
|
||||
if (connectedDevices.isEmpty()) {
|
||||
notification.setContentText(getString(R.string.foreground_notification_no_devices));
|
||||
} else {
|
||||
notification.setContentText(getString(R.string.foreground_notification_devices, TextUtils.join(", ", connectedDevices)));
|
||||
|
||||
// Adding an action button to send clipboard manually in Android 10 and later.
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P &&
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED) {
|
||||
Intent sendClipboard = ClipboardFloatingActivity.getIntent(this, true);
|
||||
PendingIntent sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard);
|
||||
}
|
||||
|
||||
if (connectedDeviceIds.size() == 1) {
|
||||
String deviceId = connectedDeviceIds.get(0);
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device != null) {
|
||||
// Adding two action buttons only when there is a single device connected.
|
||||
// Setting up Send File Intent.
|
||||
Intent sendFile = new Intent(this, SendFileActivity.class);
|
||||
sendFile.putExtra("deviceId", deviceId);
|
||||
PendingIntent sendPendingFile = PendingIntent.getActivity(this, 1, sendFile, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
notification.addAction(0, getString(R.string.send_files), sendPendingFile);
|
||||
|
||||
// Checking if there are registered commands and adding the button.
|
||||
RunCommandPlugin plugin = (RunCommandPlugin) device.getPlugin("RunCommandPlugin");
|
||||
if (plugin != null && !plugin.getCommandList().isEmpty()) {
|
||||
Intent runCommand = new Intent(this, RunCommandActivity.class);
|
||||
runCommand.putExtra("deviceId", connectedDeviceIds.get(0));
|
||||
PendingIntent runPendingCommand = PendingIntent.getActivity(this, 2, runCommand, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
notification.addAction(0, getString(R.string.pref_plugin_runcommand), runPendingCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return notification.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d("KdeConnect/BgService", "onDestroy");
|
||||
initialized = false;
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStop();
|
||||
}
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("BackgroundService");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d("KDE/BackgroundService", "onStartCommand");
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
|
||||
} else {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
}
|
||||
}
|
||||
if (intent != null && intent.getBooleanExtra("refresh", false)) {
|
||||
onNetworkChange(null);
|
||||
}
|
||||
return Service.START_STICKY;
|
||||
}
|
||||
|
||||
public static void Start(Context context) {
|
||||
Log.d("KDE/BackgroundService", "Start");
|
||||
Intent intent = new Intent(context, BackgroundService.class);
|
||||
ContextCompat.startForegroundService(context, intent);
|
||||
}
|
||||
|
||||
public static void ForceRefreshConnections(Context context) {
|
||||
Log.d("KDE/BackgroundService", "ForceRefreshConnections");
|
||||
Intent intent = new Intent(context, BackgroundService.class);
|
||||
intent.putExtra("refresh", true);
|
||||
ContextCompat.startForegroundService(context, intent);
|
||||
}
|
||||
|
||||
public void Stop() {
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
}
|
291
src/org/kde/kdeconnect/BackgroundService.kt
Normal file
291
src/org/kde/kdeconnect/BackgroundService.kt
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider.ConnectionReceiver
|
||||
import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.SendFileActivity
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity
|
||||
import org.kde.kdeconnect_tp.R
|
||||
|
||||
/**
|
||||
* This class (still) does 3 things:
|
||||
* - Keeps the app running by creating a foreground notification.
|
||||
* - Holds references to the active LinkProviders, but doesn't handle the DeviceLink those create (the KdeConnect class does that).
|
||||
* - Listens for network connectivity changes and tells the LinkProviders to re-check for devices.
|
||||
* It can be started by the KdeConnectBroadcastReceiver on some events or when the MainActivity is launched.
|
||||
*/
|
||||
class BackgroundService : Service() {
|
||||
private lateinit var applicationInstance: KdeConnect
|
||||
|
||||
private val linkProviders = ArrayList<BaseLinkProvider>()
|
||||
|
||||
private val connectedToNonCellularNetwork = MutableLiveData<Boolean>()
|
||||
/** Indicates whether device is connected over wifi / usb / bluetooth / (anything other than cellular) */
|
||||
val isConnectedToNonCellularNetwork: LiveData<Boolean>
|
||||
get() = connectedToNonCellularNetwork
|
||||
|
||||
fun updateForegroundNotification() {
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
// Update the foreground notification with the currently connected device list
|
||||
val notificationManager = getSystemService<NotificationManager>()
|
||||
notificationManager?.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification())
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerLinkProviders() {
|
||||
linkProviders.add(LanLinkProvider(this))
|
||||
// linkProviders.add(new LoopbackLinkProvider(this));
|
||||
linkProviders.add(BluetoothLinkProvider(this))
|
||||
}
|
||||
|
||||
fun onNetworkChange(network: Network?) {
|
||||
if (!initialized) {
|
||||
Log.d(LOG_TAG, "ignoring onNetworkChange called before the service is initialized")
|
||||
return
|
||||
}
|
||||
Log.d(LOG_TAG, "onNetworkChange")
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.onNetworkChange(network)
|
||||
}
|
||||
}
|
||||
|
||||
fun addConnectionListener(connectionReceiver: ConnectionReceiver) {
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.addConnectionReceiver(connectionReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeConnectionListener(connectionReceiver: ConnectionReceiver) {
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.removeConnectionReceiver(connectionReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
/** This will called only once, even if we launch the service intent several times */
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d("KdeConnect/BgService", "onCreate")
|
||||
this.applicationInstance = KdeConnect.getInstance()
|
||||
instance = this
|
||||
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("BackgroundService", this::updateForegroundNotification)
|
||||
|
||||
// Register screen on listener
|
||||
val filter = IntentFilter(Intent.ACTION_SCREEN_ON)
|
||||
// See: https://developer.android.com/reference/android/net/ConnectivityManager.html#CONNECTIVITY_ACTION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
|
||||
}
|
||||
registerReceiver(KdeConnectBroadcastReceiver(), filter)
|
||||
|
||||
// Watch for changes on all network connections except cellular networks
|
||||
val networkRequestBuilder = createNonCellularNetworkRequestBuilder()
|
||||
val connectivityManager = this.getSystemService<ConnectivityManager>()
|
||||
connectivityManager?.registerNetworkCallback(networkRequestBuilder.build(), object : NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
Log.i("BackgroundService", "Valid network available")
|
||||
connectedToNonCellularNetwork.postValue(true)
|
||||
onNetworkChange(network)
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
Log.i("BackgroundService", "Valid network lost")
|
||||
connectedToNonCellularNetwork.postValue(false)
|
||||
}
|
||||
})
|
||||
|
||||
registerLinkProviders()
|
||||
addConnectionListener(applicationInstance.connectionListener) // Link Providers need to be already registered
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.onStart()
|
||||
}
|
||||
initialized = true
|
||||
}
|
||||
|
||||
fun changePersistentNotificationVisibility(visible: Boolean) {
|
||||
if (visible) {
|
||||
updateForegroundNotification()
|
||||
}
|
||||
else {
|
||||
stopForeground(true)
|
||||
Start(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createForegroundNotification(): Notification {
|
||||
// Why is this needed: https://developer.android.com/guide/components/services#Foreground
|
||||
|
||||
val connectedDevices = ArrayList<String>()
|
||||
val connectedDeviceIds = ArrayList<String>()
|
||||
for (device in applicationInstance.devices.values) {
|
||||
if (device.isReachable && device.isPaired) {
|
||||
connectedDeviceIds.add(device.deviceId)
|
||||
connectedDevices.add(device.name)
|
||||
}
|
||||
}
|
||||
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
if (connectedDeviceIds.size == 1) {
|
||||
// Force open screen of the only connected device
|
||||
intent.putExtra(MainActivity.EXTRA_DEVICE_ID, connectedDeviceIds[0])
|
||||
}
|
||||
|
||||
val pi = PendingIntent.getActivity(this, 0, intent, UPDATE_IMMUTABLE_FLAGS)
|
||||
val notification = NotificationCompat.Builder(this, NotificationHelper.Channels.PERSISTENT).apply {
|
||||
setSmallIcon(R.drawable.ic_notification)
|
||||
setOngoing(true)
|
||||
setContentIntent(pi)
|
||||
setPriority(NotificationCompat.PRIORITY_MIN) //MIN so it's not shown in the status bar before Oreo, on Oreo it will be bumped to LOW
|
||||
setShowWhen(false)
|
||||
setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
setAutoCancel(false)
|
||||
setGroup("BackgroundService")
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
// Pre-oreo, the notification will have an empty title line without this
|
||||
notification.setContentTitle(getString(R.string.kde_connect))
|
||||
}
|
||||
|
||||
if (connectedDevices.isEmpty()) {
|
||||
notification.setContentText(getString(R.string.foreground_notification_no_devices))
|
||||
}
|
||||
else {
|
||||
notification.setContentText(getString(R.string.foreground_notification_devices, connectedDevices.joinToString(", ")))
|
||||
|
||||
// Adding an action button to send clipboard manually in Android 10 and later.
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_LOGS) == PackageManager.PERMISSION_DENIED) {
|
||||
val sendClipboard = ClipboardFloatingActivity.getIntent(this, true)
|
||||
val sendPendingClipboard = PendingIntent.getActivity(this, 3, sendClipboard, UPDATE_IMMUTABLE_FLAGS)
|
||||
notification.addAction(0, getString(R.string.foreground_notification_send_clipboard), sendPendingClipboard)
|
||||
}
|
||||
|
||||
if (connectedDeviceIds.size == 1) {
|
||||
val deviceId = connectedDeviceIds[0]
|
||||
val device = KdeConnect.getInstance().getDevice(deviceId)
|
||||
if (device != null) {
|
||||
// Adding two action buttons only when there is a single device connected.
|
||||
// Setting up Send File Intent.
|
||||
val sendFile = Intent(this, SendFileActivity::class.java)
|
||||
sendFile.putExtra("deviceId", deviceId)
|
||||
val sendPendingFile = PendingIntent.getActivity(this, 1, sendFile, UPDATE_IMMUTABLE_FLAGS)
|
||||
notification.addAction(0, getString(R.string.send_files), sendPendingFile)
|
||||
|
||||
// Checking if there are registered commands and adding the button.
|
||||
val plugin = device.getPlugin("RunCommandPlugin") as RunCommandPlugin?
|
||||
if (plugin != null && plugin.commandList.isNotEmpty()) {
|
||||
val runCommand = Intent(this, RunCommandActivity::class.java)
|
||||
runCommand.putExtra("deviceId", connectedDeviceIds[0])
|
||||
val runPendingCommand = PendingIntent.getActivity(this, 2, runCommand, UPDATE_IMMUTABLE_FLAGS)
|
||||
notification.addAction(0, getString(R.string.pref_plugin_runcommand), runPendingCommand)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return notification.build()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d("KdeConnect/BgService", "onDestroy")
|
||||
initialized = false
|
||||
for (linkProvider in linkProviders) {
|
||||
linkProvider.onStop()
|
||||
}
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("BackgroundService")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? = null
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.d(LOG_TAG, "onStartCommand")
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
|
||||
}
|
||||
else {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification())
|
||||
}
|
||||
}
|
||||
if (intent != null && intent.getBooleanExtra("refresh", false)) {
|
||||
onNetworkChange(null)
|
||||
}
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOG_TAG = "KDE/BackgroundService"
|
||||
|
||||
const val UPDATE_IMMUTABLE_FLAGS = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
private const val FOREGROUND_NOTIFICATION_ID = 1
|
||||
|
||||
@JvmStatic
|
||||
var instance: BackgroundService? = null
|
||||
private set
|
||||
|
||||
private var initialized = false
|
||||
|
||||
private fun createNonCellularNetworkRequestBuilder(): NetworkRequest.Builder {
|
||||
return NetworkRequest.Builder().apply {
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_VPN)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S) {
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_USB)
|
||||
addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Start(context: Context) {
|
||||
Log.d(LOG_TAG, "Start")
|
||||
val intent = Intent(context, BackgroundService::class.java)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun ForceRefreshConnections(context: Context) {
|
||||
Log.d(LOG_TAG, "ForceRefreshConnections")
|
||||
val intent = Intent(context, BackgroundService::class.java)
|
||||
intent.putExtra("refresh", true)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
}
|
||||
}
|
@@ -112,7 +112,7 @@ class Device : PacketReceiver {
|
||||
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE)
|
||||
this.deviceInfo = loadFromSettings(context, deviceId, settings)
|
||||
this.pairingHandler = PairingHandler(this, createDefaultPairingCallback(), PairingHandler.PairState.Paired)
|
||||
this.supportedPlugins = Vector(PluginFactory.getAvailablePlugins()) // Assume all are supported until we receive capabilities
|
||||
this.supportedPlugins = Vector(PluginFactory.availablePlugins) // Assume all are supported until we receive capabilities
|
||||
Log.i("Device", "Loading trusted device: ${deviceInfo.name}")
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ class Device : PacketReceiver {
|
||||
this.deviceInfo = link.deviceInfo
|
||||
this.settings = context.getSharedPreferences(deviceInfo.id, Context.MODE_PRIVATE)
|
||||
this.pairingHandler = PairingHandler(this, createDefaultPairingCallback(), PairingHandler.PairState.NotPaired)
|
||||
this.supportedPlugins = Vector(PluginFactory.getAvailablePlugins()) // Assume all are supported until we receive capabilities
|
||||
this.supportedPlugins = Vector(PluginFactory.availablePlugins) // Assume all are supported until we receive capabilities
|
||||
Log.i("Device", "Creating untrusted device: " + deviceInfo.name)
|
||||
addLink(link)
|
||||
}
|
||||
@@ -257,7 +257,7 @@ class Device : PacketReceiver {
|
||||
context,
|
||||
1,
|
||||
intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
|
||||
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val acceptIntent = Intent(context, MainActivity::class.java).apply {
|
||||
@@ -273,13 +273,13 @@ class Device : PacketReceiver {
|
||||
context,
|
||||
2,
|
||||
acceptIntent,
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_MUTABLE
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
val rejectedPendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
4,
|
||||
rejectIntent,
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_MUTABLE
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val res = context.resources
|
||||
@@ -622,7 +622,7 @@ class Device : PacketReceiver {
|
||||
|
||||
supportedPlugins.forEach { pluginKey ->
|
||||
val pluginInfo = PluginFactory.getPluginInfo(pluginKey)
|
||||
val listenToUnpaired = pluginInfo.listenToUnpaired()
|
||||
val listenToUnpaired = pluginInfo.listenToUnpaired
|
||||
|
||||
val pluginEnabled = (isPaired || listenToUnpaired) && this.isReachable && isPluginEnabled(pluginKey)
|
||||
|
||||
|
@@ -22,13 +22,13 @@ import java.security.cert.CertificateException
|
||||
* DeviceInfo contains all the properties needed to instantiate a Device.
|
||||
*/
|
||||
class DeviceInfo(
|
||||
@JvmField val id : String,
|
||||
@JvmField val certificate : Certificate,
|
||||
@JvmField var name : String,
|
||||
@JvmField var type : DeviceType,
|
||||
@JvmField var protocolVersion : Int = 0,
|
||||
@JvmField var incomingCapabilities : Set<String>? = null,
|
||||
@JvmField var outgoingCapabilities : Set<String>? = null,
|
||||
@JvmField val id: String,
|
||||
@JvmField val certificate: Certificate,
|
||||
@JvmField var name: String,
|
||||
@JvmField var type: DeviceType,
|
||||
@JvmField var protocolVersion: Int = 0,
|
||||
@JvmField var incomingCapabilities: Set<String>? = null,
|
||||
@JvmField var outgoingCapabilities: Set<String>? = null,
|
||||
) {
|
||||
|
||||
/**
|
||||
@@ -40,7 +40,7 @@ class DeviceInfo(
|
||||
try {
|
||||
val encodedCertificate = Base64.encodeToString(certificate.encoded, 0)
|
||||
|
||||
with (settings.edit()) {
|
||||
with(settings.edit()) {
|
||||
putString("certificate", encodedCertificate)
|
||||
putString("deviceName", name)
|
||||
putString("deviceType", type.toString())
|
||||
@@ -73,7 +73,7 @@ class DeviceInfo(
|
||||
*/
|
||||
@JvmStatic
|
||||
@Throws(CertificateException::class)
|
||||
fun loadFromSettings(context : Context, deviceId: String, settings: SharedPreferences) =
|
||||
fun loadFromSettings(context: Context, deviceId: String, settings: SharedPreferences) =
|
||||
with(settings) {
|
||||
DeviceInfo(
|
||||
id = deviceId,
|
||||
@@ -104,8 +104,8 @@ class DeviceInfo(
|
||||
@JvmStatic
|
||||
fun isValidIdentityPacket(identityPacket: NetworkPacket): Boolean = with(identityPacket) {
|
||||
type == NetworkPacket.PACKET_TYPE_IDENTITY &&
|
||||
DeviceHelper.filterName(getString("deviceName", "")).isNotBlank() &&
|
||||
getString("deviceId", "").isNotBlank()
|
||||
DeviceHelper.filterName(getString("deviceName", "")).isNotBlank() &&
|
||||
getString("deviceId", "").isNotBlank()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ enum class DeviceType {
|
||||
ContextCompat.getDrawable(context, toDrawableId())!!
|
||||
|
||||
@DrawableRes
|
||||
private fun toDrawableId() =
|
||||
fun toDrawableId() =
|
||||
when (this) {
|
||||
PHONE -> R.drawable.ic_device_phone_32dp
|
||||
TABLET -> R.drawable.ic_device_tablet_32dp
|
||||
@@ -135,6 +135,15 @@ enum class DeviceType {
|
||||
else -> R.drawable.ic_device_desktop_32dp
|
||||
}
|
||||
|
||||
fun toShortcutDrawableId() =
|
||||
when (this) {
|
||||
PHONE -> R.drawable.ic_device_phone_shortcut
|
||||
TABLET -> R.drawable.ic_device_tablet_shortcut
|
||||
TV -> R.drawable.ic_device_tv_shortcut
|
||||
LAPTOP -> R.drawable.ic_device_laptop_shortcut
|
||||
else -> R.drawable.ic_device_desktop_shortcut
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromString(s: String) =
|
||||
|
871
src/org/kde/kdeconnect/Helpers/CollectionsBackport.java
Normal file
871
src/org/kde/kdeconnect/Helpers/CollectionsBackport.java
Normal file
@@ -0,0 +1,871 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 The Android Open Source Project
|
||||
* SPDX-FileCopyrightText: 1997, 2021, Oracle and/or its affiliates. All rights reserved
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024 ShellWen Chen <me@shellwen.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.Spliterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/** @noinspection unused*/
|
||||
public final class CollectionsBackport {
|
||||
public static <T> NavigableSet<T> unmodifiableNavigableSet(NavigableSet<T> s) {
|
||||
if (VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return Collections.unmodifiableNavigableSet(s);
|
||||
} else {
|
||||
return new UnmodifiableNavigableSetBackport<>(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Set<T> unmodifiableSet(Set<T> s) {
|
||||
if (VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return Collections.unmodifiableSet(s);
|
||||
} else {
|
||||
return new UnmodifiableSetBackport<>(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Collection<T> unmodifiableCollection(Collection<T> c) {
|
||||
if (VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return Collections.unmodifiableCollection(c);
|
||||
} else {
|
||||
return new UnmodifiableCollectionBackport<>(c);
|
||||
}
|
||||
}
|
||||
|
||||
public static <K, V> NavigableMap<K, V> unmodifiableNavigableMap(NavigableMap<K, V> m) {
|
||||
if (VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return Collections.unmodifiableNavigableMap(m);
|
||||
} else {
|
||||
return new UnmodifiableNavigableMapBackport<>(m);
|
||||
}
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> unmodifiableMap(Map<K, V> m) {
|
||||
if (VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return Collections.unmodifiableMap(m);
|
||||
} else {
|
||||
return new UnmodifiableMapBackport<>(m);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> NavigableSet<T> emptyNavigableSet() {
|
||||
//noinspection unchecked
|
||||
return (NavigableSet<T>) UnmodifiableNavigableSetBackport.EMPTY_NAVIGABLE_SET;
|
||||
}
|
||||
|
||||
public static <K, V> NavigableMap<K, V> emptyNavigableMap() {
|
||||
//noinspection unchecked
|
||||
return (NavigableMap<K, V>) UnmodifiableNavigableMapBackport.EMPTY_NAVIGABLE_MAP;
|
||||
}
|
||||
|
||||
static boolean eq(Object o1, Object o2) {
|
||||
return o1 == null ? o2 == null : o1.equals(o2);
|
||||
}
|
||||
|
||||
static class UnmodifiableNavigableSetBackport<E>
|
||||
extends UnmodifiableSortedSetBackport<E>
|
||||
implements NavigableSet<E>, Serializable {
|
||||
|
||||
/**
|
||||
* A singleton empty unmodifiable navigable set used for
|
||||
* {@link #emptyNavigableSet()}.
|
||||
*
|
||||
* @param <E> type of elements, if there were any, and bounds
|
||||
*/
|
||||
private static class EmptyNavigableSet<E> extends UnmodifiableNavigableSetBackport<E>
|
||||
implements Serializable {
|
||||
public EmptyNavigableSet() {
|
||||
super(new TreeSet<>());
|
||||
}
|
||||
|
||||
@java.io.Serial
|
||||
private Object readResolve() {
|
||||
return EMPTY_NAVIGABLE_SET;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final NavigableSet<?> EMPTY_NAVIGABLE_SET =
|
||||
new EmptyNavigableSet<>();
|
||||
|
||||
/**
|
||||
* The instance we are protecting.
|
||||
*/
|
||||
@SuppressWarnings("serial") // Conditionally serializable
|
||||
private final NavigableSet<E> ns;
|
||||
|
||||
UnmodifiableNavigableSetBackport(NavigableSet<E> s) {
|
||||
super(s);
|
||||
ns = s;
|
||||
}
|
||||
|
||||
public E lower(E e) {
|
||||
return ns.lower(e);
|
||||
}
|
||||
|
||||
public E floor(E e) {
|
||||
return ns.floor(e);
|
||||
}
|
||||
|
||||
public E ceiling(E e) {
|
||||
return ns.ceiling(e);
|
||||
}
|
||||
|
||||
public E higher(E e) {
|
||||
return ns.higher(e);
|
||||
}
|
||||
|
||||
public E pollFirst() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public E pollLast() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public NavigableSet<E> descendingSet() {
|
||||
return new UnmodifiableNavigableSetBackport<>(ns.descendingSet());
|
||||
}
|
||||
|
||||
public Iterator<E> descendingIterator() {
|
||||
return descendingSet().iterator();
|
||||
}
|
||||
|
||||
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
|
||||
return new UnmodifiableNavigableSetBackport<>(
|
||||
ns.subSet(fromElement, fromInclusive, toElement, toInclusive));
|
||||
}
|
||||
|
||||
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
|
||||
return new UnmodifiableNavigableSetBackport<>(
|
||||
ns.headSet(toElement, inclusive));
|
||||
}
|
||||
|
||||
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
|
||||
return new UnmodifiableNavigableSetBackport<>(
|
||||
ns.tailSet(fromElement, inclusive));
|
||||
}
|
||||
}
|
||||
|
||||
static class UnmodifiableSortedSetBackport<E>
|
||||
extends UnmodifiableSetBackport<E>
|
||||
implements SortedSet<E>, Serializable {
|
||||
@SuppressWarnings("serial") // Conditionally serializable
|
||||
private final SortedSet<E> ss;
|
||||
|
||||
UnmodifiableSortedSetBackport(SortedSet<E> s) {
|
||||
super(s);
|
||||
ss = s;
|
||||
}
|
||||
|
||||
public Comparator<? super E> comparator() {
|
||||
return ss.comparator();
|
||||
}
|
||||
|
||||
public SortedSet<E> subSet(E fromElement, E toElement) {
|
||||
return new UnmodifiableSortedSetBackport<>(ss.subSet(fromElement, toElement));
|
||||
}
|
||||
|
||||
public SortedSet<E> headSet(E toElement) {
|
||||
return new UnmodifiableSortedSetBackport<>(ss.headSet(toElement));
|
||||
}
|
||||
|
||||
public SortedSet<E> tailSet(E fromElement) {
|
||||
return new UnmodifiableSortedSetBackport<>(ss.tailSet(fromElement));
|
||||
}
|
||||
|
||||
public E first() {
|
||||
return ss.first();
|
||||
}
|
||||
|
||||
public E last() {
|
||||
return ss.last();
|
||||
}
|
||||
}
|
||||
|
||||
static class UnmodifiableSetBackport<E> extends UnmodifiableCollectionBackport<E>
|
||||
implements Set<E>, Serializable {
|
||||
|
||||
UnmodifiableSetBackport(Set<? extends E> s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return o == this || c.equals(o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return c.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
static class UnmodifiableCollectionBackport<E> implements Collection<E>, Serializable {
|
||||
|
||||
@SuppressWarnings("serial") // Conditionally serializable
|
||||
final Collection<? extends E> c;
|
||||
|
||||
UnmodifiableCollectionBackport(Collection<? extends E> c) {
|
||||
if (c == null)
|
||||
throw new NullPointerException();
|
||||
this.c = c;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return c.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return c.isEmpty();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
return c.contains(o);
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
return c.toArray();
|
||||
}
|
||||
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return c.toArray(a);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
|
||||
public <T> T[] toArray(IntFunction<T[]> f) {
|
||||
return c.toArray(f);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return c.toString();
|
||||
}
|
||||
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<E>() {
|
||||
private final Iterator<? extends E> i = c.iterator();
|
||||
|
||||
public boolean hasNext() {
|
||||
return i.hasNext();
|
||||
}
|
||||
|
||||
public E next() {
|
||||
return i.next();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachRemaining(Consumer<? super E> action) {
|
||||
// Use backing collection version
|
||||
i.forEachRemaining(action);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean add(E e) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<?> coll) {
|
||||
return c.containsAll(coll);
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends E> coll) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean removeAll(Collection<?> coll) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean retainAll(Collection<?> coll) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Override default methods in Collection
|
||||
@Override
|
||||
public void forEach(Consumer<? super E> action) {
|
||||
c.forEach(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeIf(Predicate<? super E> filter) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return (Spliterator<E>) c.spliterator();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Stream<E> stream() {
|
||||
return (Stream<E>) c.stream();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Stream<E> parallelStream() {
|
||||
return (Stream<E>) c.parallelStream();
|
||||
}
|
||||
}
|
||||
|
||||
static class UnmodifiableNavigableMapBackport<K, V> extends UnmodifiableSortedMapBackport<K, V> implements NavigableMap<K, V>, Serializable {
|
||||
private static final long serialVersionUID = -4858195264774772197L;
|
||||
private static final EmptyNavigableMapBackport<?, ?> EMPTY_NAVIGABLE_MAP = new EmptyNavigableMapBackport();
|
||||
private final NavigableMap<K, ? extends V> nm;
|
||||
|
||||
UnmodifiableNavigableMapBackport(NavigableMap<K, ? extends V> m) {
|
||||
super(m);
|
||||
this.nm = m;
|
||||
}
|
||||
|
||||
public K lowerKey(K key) {
|
||||
return this.nm.lowerKey(key);
|
||||
}
|
||||
|
||||
public K floorKey(K key) {
|
||||
return this.nm.floorKey(key);
|
||||
}
|
||||
|
||||
public K ceilingKey(K key) {
|
||||
return this.nm.ceilingKey(key);
|
||||
}
|
||||
|
||||
public K higherKey(K key) {
|
||||
return this.nm.higherKey(key);
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> lowerEntry(K key) {
|
||||
Map.Entry<K, V> lower = (Entry<K, V>) this.nm.lowerEntry(key);
|
||||
return null != lower ? new UnmodifiableMapBackport.UnmodifiableEntrySetBackport.UnmodifiableEntry(lower) : null;
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> floorEntry(K key) {
|
||||
Map.Entry<K, V> floor = (Entry<K, V>) this.nm.floorEntry(key);
|
||||
return null != floor ? new UnmodifiableMapBackport.UnmodifiableEntrySetBackport.UnmodifiableEntry(floor) : null;
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> ceilingEntry(K key) {
|
||||
Map.Entry<K, V> ceiling = (Entry<K, V>) this.nm.ceilingEntry(key);
|
||||
return null != ceiling ? new UnmodifiableMapBackport.UnmodifiableEntrySetBackport.UnmodifiableEntry(ceiling) : null;
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> higherEntry(K key) {
|
||||
Map.Entry<K, V> higher = (Entry<K, V>) this.nm.higherEntry(key);
|
||||
return null != higher ? new UnmodifiableMapBackport.UnmodifiableEntrySetBackport.UnmodifiableEntry(higher) : null;
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> firstEntry() {
|
||||
Map.Entry<K, V> first = (Entry<K, V>) this.nm.firstEntry();
|
||||
return null != first ? new UnmodifiableMapBackport.UnmodifiableEntrySetBackport.UnmodifiableEntry(first) : null;
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> lastEntry() {
|
||||
Map.Entry<K, V> last = (Entry<K, V>) this.nm.lastEntry();
|
||||
return null != last ? new UnmodifiableMapBackport.UnmodifiableEntrySetBackport.UnmodifiableEntry(last) : null;
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> pollFirstEntry() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> pollLastEntry() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public NavigableMap<K, V> descendingMap() {
|
||||
return (NavigableMap<K, V>) CollectionsBackport.unmodifiableNavigableMap(this.nm.descendingMap());
|
||||
}
|
||||
|
||||
public NavigableSet<K> navigableKeySet() {
|
||||
return CollectionsBackport.unmodifiableNavigableSet(this.nm.navigableKeySet());
|
||||
}
|
||||
|
||||
public NavigableSet<K> descendingKeySet() {
|
||||
return CollectionsBackport.unmodifiableNavigableSet(this.nm.descendingKeySet());
|
||||
}
|
||||
|
||||
public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) {
|
||||
return (NavigableMap<K, V>) CollectionsBackport.unmodifiableNavigableMap(this.nm.subMap(fromKey, fromInclusive, toKey, toInclusive));
|
||||
}
|
||||
|
||||
public NavigableMap<K, V> headMap(K toKey, boolean inclusive) {
|
||||
return (NavigableMap<K, V>) CollectionsBackport.unmodifiableNavigableMap(this.nm.headMap(toKey, inclusive));
|
||||
}
|
||||
|
||||
public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) {
|
||||
return (NavigableMap<K, V>) CollectionsBackport.unmodifiableNavigableMap(this.nm.tailMap(fromKey, inclusive));
|
||||
}
|
||||
|
||||
private static class EmptyNavigableMapBackport<K, V> extends UnmodifiableNavigableMapBackport<K, V> implements Serializable {
|
||||
private static final long serialVersionUID = -2239321462712562324L;
|
||||
|
||||
EmptyNavigableMapBackport() {
|
||||
super(new TreeMap());
|
||||
}
|
||||
|
||||
public NavigableSet<K> navigableKeySet() {
|
||||
return CollectionsBackport.emptyNavigableSet();
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return UnmodifiableNavigableMapBackport.EMPTY_NAVIGABLE_MAP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class UnmodifiableSortedMapBackport<K,V>
|
||||
extends UnmodifiableMapBackport<K,V>
|
||||
implements SortedMap<K,V>, Serializable {
|
||||
@SuppressWarnings("serial") // Conditionally serializable
|
||||
private final SortedMap<K, ? extends V> sm;
|
||||
|
||||
UnmodifiableSortedMapBackport(SortedMap<K, ? extends V> m) {super(m); sm = m; }
|
||||
public Comparator<? super K> comparator() { return sm.comparator(); }
|
||||
public SortedMap<K,V> subMap(K fromKey, K toKey)
|
||||
{ return new UnmodifiableSortedMapBackport<>(sm.subMap(fromKey, toKey)); }
|
||||
public SortedMap<K,V> headMap(K toKey)
|
||||
{ return new UnmodifiableSortedMapBackport<>(sm.headMap(toKey)); }
|
||||
public SortedMap<K,V> tailMap(K fromKey)
|
||||
{ return new UnmodifiableSortedMapBackport<>(sm.tailMap(fromKey)); }
|
||||
public K firstKey() { return sm.firstKey(); }
|
||||
public K lastKey() { return sm.lastKey(); }
|
||||
}
|
||||
|
||||
private static class UnmodifiableMapBackport<K, V> implements Map<K, V>, Serializable {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = -1034234728574286014L;
|
||||
|
||||
@SuppressWarnings("serial") // Conditionally serializable
|
||||
private final Map<? extends K, ? extends V> m;
|
||||
|
||||
UnmodifiableMapBackport(Map<? extends K, ? extends V> m) {
|
||||
if (m == null)
|
||||
throw new NullPointerException();
|
||||
this.m = m;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return m.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return m.isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return m.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object val) {
|
||||
return m.containsValue(val);
|
||||
}
|
||||
|
||||
public V get(Object key) {
|
||||
return m.get(key);
|
||||
}
|
||||
|
||||
public V put(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public V remove(Object key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private transient Set<K> keySet;
|
||||
private transient Set<Map.Entry<K, V>> entrySet;
|
||||
private transient Collection<V> values;
|
||||
|
||||
public Set<K> keySet() {
|
||||
if (keySet == null)
|
||||
keySet = (Set<K>) unmodifiableSet(m.keySet());
|
||||
return keySet;
|
||||
}
|
||||
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
if (entrySet == null)
|
||||
entrySet = new UnmodifiableEntrySetBackport<>(m.entrySet());
|
||||
return entrySet;
|
||||
}
|
||||
|
||||
public Collection<V> values() {
|
||||
if (values == null)
|
||||
values = (Collection<V>) unmodifiableCollection(m.values());
|
||||
return values;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return o == this || m.equals(o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return m.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return m.toString();
|
||||
}
|
||||
|
||||
// Override default methods in Map
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V getOrDefault(Object k, V defaultValue) {
|
||||
// Safe cast as we don't change the value
|
||||
return ((Map<K, V>) m).getOrDefault(k, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BiConsumer<? super K, ? super V> action) {
|
||||
m.forEach(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V putIfAbsent(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replace(K key, V oldValue, V newValue) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V replace(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V computeIfPresent(K key,
|
||||
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V compute(K key,
|
||||
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V merge(K key, V value,
|
||||
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* We need this class in addition to UnmodifiableSet as
|
||||
* Map.Entries themselves permit modification of the backing Map
|
||||
* via their setValue operation. This class is subtle: there are
|
||||
* many possible attacks that must be thwarted.
|
||||
*
|
||||
* @serial include
|
||||
*/
|
||||
static class UnmodifiableEntrySetBackport<K, V>
|
||||
extends UnmodifiableSetBackport<Map.Entry<K, V>> {
|
||||
@java.io.Serial
|
||||
private static final long serialVersionUID = 7854390611657943733L;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
UnmodifiableEntrySetBackport(Set<? extends Map.Entry<? extends K, ? extends V>> s) {
|
||||
// Need to cast to raw in order to work around a limitation in the type system
|
||||
super((Set) s);
|
||||
}
|
||||
|
||||
static <K, V> Consumer<Map.Entry<? extends K, ? extends V>> entryConsumer(
|
||||
Consumer<? super Entry<K, V>> action) {
|
||||
return e -> action.accept(new UnmodifiableEntry<>(e));
|
||||
}
|
||||
|
||||
public void forEach(Consumer<? super Entry<K, V>> action) {
|
||||
Objects.requireNonNull(action);
|
||||
c.forEach(entryConsumer(action));
|
||||
}
|
||||
|
||||
static final class UnmodifiableEntrySetSpliterator<K, V>
|
||||
implements Spliterator<Entry<K, V>> {
|
||||
final Spliterator<Map.Entry<K, V>> s;
|
||||
|
||||
UnmodifiableEntrySetSpliterator(Spliterator<Entry<K, V>> s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super Entry<K, V>> action) {
|
||||
Objects.requireNonNull(action);
|
||||
return s.tryAdvance(entryConsumer(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachRemaining(Consumer<? super Entry<K, V>> action) {
|
||||
Objects.requireNonNull(action);
|
||||
s.forEachRemaining(entryConsumer(action));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<Entry<K, V>> trySplit() {
|
||||
Spliterator<Entry<K, V>> split = s.trySplit();
|
||||
return split == null
|
||||
? null
|
||||
: new UnmodifiableEntrySetSpliterator<>(split);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return s.estimateSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExactSizeIfKnown() {
|
||||
return s.getExactSizeIfKnown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return s.characteristics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCharacteristics(int characteristics) {
|
||||
return s.hasCharacteristics(characteristics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super Entry<K, V>> getComparator() {
|
||||
return s.getComparator();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Spliterator<Entry<K, V>> spliterator() {
|
||||
return new UnmodifiableEntrySetSpliterator<>(
|
||||
(Spliterator<Map.Entry<K, V>>) c.spliterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Entry<K, V>> stream() {
|
||||
return StreamSupport.stream(spliterator(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Entry<K, V>> parallelStream() {
|
||||
return StreamSupport.stream(spliterator(), true);
|
||||
}
|
||||
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new Iterator<Map.Entry<K, V>>() {
|
||||
private final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = c.iterator();
|
||||
|
||||
public boolean hasNext() {
|
||||
return i.hasNext();
|
||||
}
|
||||
|
||||
public Map.Entry<K, V> next() {
|
||||
return new UnmodifiableEntry<>(i.next());
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Seems like an oversight. http://b/110351017
|
||||
public void forEachRemaining(Consumer<? super Map.Entry<K, V>> action) {
|
||||
i.forEachRemaining(entryConsumer(action));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object[] toArray() {
|
||||
Object[] a = c.toArray();
|
||||
for (int i = 0; i < a.length; i++)
|
||||
a[i] = new UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>) a[i]);
|
||||
return a;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T[] toArray(T[] a) {
|
||||
// We don't pass a to c.toArray, to avoid window of
|
||||
// vulnerability wherein an unscrupulous multithreaded client
|
||||
// could get his hands on raw (unwrapped) Entries from c.
|
||||
Object[] arr = c.toArray(a.length == 0 ? a : Arrays.copyOf(a, 0));
|
||||
|
||||
for (int i = 0; i < arr.length; i++)
|
||||
arr[i] = new UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>) arr[i]);
|
||||
|
||||
if (arr.length > a.length)
|
||||
return (T[]) arr;
|
||||
|
||||
System.arraycopy(arr, 0, a, 0, arr.length);
|
||||
if (a.length > arr.length)
|
||||
a[arr.length] = null;
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is overridden to protect the backing set against
|
||||
* an object with a nefarious equals function that senses
|
||||
* that the equality-candidate is Map.Entry and calls its
|
||||
* setValue method.
|
||||
*/
|
||||
public boolean contains(Object o) {
|
||||
if (!(o instanceof Map.Entry))
|
||||
return false;
|
||||
return c.contains(
|
||||
new UnmodifiableEntry<>((Map.Entry<?, ?>) o));
|
||||
}
|
||||
|
||||
/**
|
||||
* The next two methods are overridden to protect against
|
||||
* an unscrupulous List whose contains(Object o) method senses
|
||||
* when o is a Map.Entry, and calls o.setValue.
|
||||
*/
|
||||
public boolean containsAll(Collection<?> coll) {
|
||||
for (Object e : coll) {
|
||||
if (!contains(e)) // Invokes safe contains() above
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
|
||||
// Android-changed: (b/247094511) instanceof pattern variable is not yet supported.
|
||||
/*
|
||||
return o instanceof Set<?> s
|
||||
&& s.size() == c.size()
|
||||
&& containsAll(s); // Invokes safe containsAll() above
|
||||
*/
|
||||
if (!(o instanceof Set))
|
||||
return false;
|
||||
Set<?> s = (Set<?>) o;
|
||||
if (s.size() != c.size())
|
||||
return false;
|
||||
return containsAll(s); // Invokes safe containsAll() above
|
||||
}
|
||||
|
||||
/**
|
||||
* This "wrapper class" serves two purposes: it prevents
|
||||
* the client from modifying the backing Map, by short-circuiting
|
||||
* the setValue method, and it protects the backing Map against
|
||||
* an ill-behaved Map.Entry that attempts to modify another
|
||||
* Map Entry when asked to perform an equality check.
|
||||
*/
|
||||
private static class UnmodifiableEntry<K, V> implements Map.Entry<K, V> {
|
||||
private Map.Entry<? extends K, ? extends V> e;
|
||||
|
||||
UnmodifiableEntry(Map.Entry<? extends K, ? extends V> e) {
|
||||
this.e = Objects.requireNonNull(e);
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return e.getKey();
|
||||
}
|
||||
|
||||
public V getValue() {
|
||||
return e.getValue();
|
||||
}
|
||||
|
||||
public V setValue(V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return e.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
// Android-changed: (b/247094511) instanceof pattern variable is not yet
|
||||
// supported.
|
||||
/*
|
||||
return o instanceof Map.Entry<?, ?> t
|
||||
&& eq(e.getKey(), t.getKey())
|
||||
&& eq(e.getValue(), t.getValue());
|
||||
*/
|
||||
if (!(o instanceof Map.Entry))
|
||||
return false;
|
||||
Map.Entry<?, ?> t = (Map.Entry<?, ?>) o;
|
||||
return eq(e.getKey(), t.getKey()) &&
|
||||
eq(e.getValue(), t.getValue());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return e.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -160,8 +160,8 @@ object DeviceHelper {
|
||||
getDeviceName(context),
|
||||
deviceType,
|
||||
ProtocolVersion,
|
||||
PluginFactory.getIncomingCapabilities(),
|
||||
PluginFactory.getOutgoingCapabilities()
|
||||
PluginFactory.incomingCapabilities,
|
||||
PluginFactory.outgoingCapabilities
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FilesHelper {
|
||||
public static final String LOG_TAG = "SendFileActivity";
|
||||
|
||||
public static String getMimeTypeFromFile(String file) {
|
||||
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(FilenameUtils.getExtension(file));
|
||||
return StringUtils.defaultString(mime, "*/*");
|
||||
}
|
||||
|
||||
public static String findNonExistingNameForNewFile(String path, String filename) {
|
||||
String name = FilenameUtils.getBaseName(filename);
|
||||
String ext = FilenameUtils.getExtension(filename);
|
||||
|
||||
int num = 1;
|
||||
while (new File(path + "/" + filename).exists()) {
|
||||
filename = name + " (" + num + ")." + ext;
|
||||
num++;
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
//Following code from http://activemq.apache.org/maven/5.7.0/kahadb/apidocs/src-html/org/apache/kahadb/util/IOHelper.html
|
||||
|
||||
/**
|
||||
* Converts any string into a string that is safe to use as a file name.
|
||||
* The result will only include ascii characters and numbers, and the "-","_", and "." characters.
|
||||
*/
|
||||
private static String toFileSystemSafeName(String name, boolean dirSeparators, int maxFileLength) {
|
||||
int size = name.length();
|
||||
StringBuilder rc = new StringBuilder(size * 2);
|
||||
for (int i = 0; i < size; i++) {
|
||||
char c = name.charAt(i);
|
||||
boolean valid = c >= 'a' && c <= 'z';
|
||||
valid = valid || (c >= 'A' && c <= 'Z');
|
||||
valid = valid || (c >= '0' && c <= '9');
|
||||
valid = valid || (c == '_') || (c == '-') || (c == '.');
|
||||
valid = valid || (dirSeparators && ((c == '/') || (c == '\\')));
|
||||
|
||||
if (valid) {
|
||||
rc.append(c);
|
||||
}
|
||||
}
|
||||
String result = rc.toString();
|
||||
if (result.length() > maxFileLength) {
|
||||
result = result.substring(result.length() - maxFileLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String toFileSystemSafeName(String name, boolean dirSeparators) {
|
||||
return toFileSystemSafeName(name, dirSeparators, 255);
|
||||
}
|
||||
|
||||
public static String toFileSystemSafeName(String name) {
|
||||
return toFileSystemSafeName(name, true, 255);
|
||||
}
|
||||
|
||||
private static int GetOpenFileCount() {
|
||||
return new File("/proc/self/fd").listFiles().length;
|
||||
}
|
||||
|
||||
public static void LogOpenFileCount() {
|
||||
Log.e("KDE/FileCount", "" + GetOpenFileCount());
|
||||
}
|
||||
|
||||
|
||||
//Create the network packet from the URI
|
||||
public static NetworkPacket uriToNetworkPacket(final Context context, final Uri uri, String type) {
|
||||
|
||||
try {
|
||||
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
InputStream inputStream = cr.openInputStream(uri);
|
||||
|
||||
NetworkPacket np = new NetworkPacket(type);
|
||||
|
||||
String filename = null;
|
||||
long size = -1;
|
||||
Long lastModified = null;
|
||||
|
||||
if (uri.getScheme().equals("file")) {
|
||||
// file:// is a non media uri, so we cannot query the ContentProvider
|
||||
|
||||
try {
|
||||
File mFile = new File(uri.getPath());
|
||||
|
||||
filename = mFile.getName();
|
||||
size = mFile.length();
|
||||
lastModified = mFile.lastModified();
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(LOG_TAG, "Received bad file URI", e);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Since we used Intent.CATEGORY_OPENABLE, these two columns are the only ones we are
|
||||
// guaranteed to have: https://developer.android.com/reference/android/provider/OpenableColumns
|
||||
String[] proj = {
|
||||
OpenableColumns.SIZE,
|
||||
OpenableColumns.DISPLAY_NAME,
|
||||
};
|
||||
|
||||
try (Cursor cursor = cr.query(uri, proj, null, null, null)) {
|
||||
int nameColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
int sizeColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
|
||||
cursor.moveToFirst();
|
||||
|
||||
filename = cursor.getString(nameColumnIndex);
|
||||
|
||||
// It is recommended to check for the value to be null because there are
|
||||
// situations were we don't know the size (for instance, if the file is
|
||||
// not local to the device)
|
||||
if (!cursor.isNull(sizeColumnIndex)) {
|
||||
size = cursor.getLong(sizeColumnIndex);
|
||||
}
|
||||
|
||||
lastModified = getLastModifiedTime(context, uri);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Problem getting file information", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (filename != null) {
|
||||
np.set("filename", filename);
|
||||
} else {
|
||||
// It would be very surprising if this happens
|
||||
Log.e(LOG_TAG, "Unable to read filename");
|
||||
}
|
||||
|
||||
if (lastModified != null) {
|
||||
np.set("lastModified", lastModified);
|
||||
} else {
|
||||
// This would not be too surprising, and probably means we need to improve
|
||||
// FilesHelper.getLastModifiedTime
|
||||
Log.w(LOG_TAG, "Unable to read file last modified time");
|
||||
}
|
||||
|
||||
np.setPayload(new NetworkPacket.Payload(inputStream, size));
|
||||
|
||||
return np;
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Exception creating network packet", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By hook or by crook, get the last modified time of the passed content:// URI
|
||||
*
|
||||
* This is a challenge because different content sources have different columns defined, and
|
||||
* I don't know how to tell what the source of the content is.
|
||||
*
|
||||
* Therefore, my brilliant solution is to just try everything until something works.
|
||||
*
|
||||
* Will return null if nothing worked.
|
||||
*/
|
||||
public static Long getLastModifiedTime(final Context context, final Uri uri) {
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
|
||||
Long lastModifiedTime = null;
|
||||
|
||||
// Open a cursor without a column because we do not yet know what columns are defined
|
||||
try (Cursor cursor = cr.query(uri, null, null, null, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
String[] allColumns = cursor.getColumnNames();
|
||||
|
||||
// MediaStore.MediaColumns.DATE_MODIFIED resolves to "date_modified"
|
||||
// I see this column defined in case we used the Gallery app to select the file to transfer
|
||||
// This can occur both for devices running Storage Access Framework (SAF) if we select
|
||||
// the Gallery to provide the file to transfer, as well as for older devices by doing the same
|
||||
int mediaDataModifiedColumnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED);
|
||||
|
||||
// DocumentsContract.Document.COLUMN_LAST_MODIFIED resolves to "last_modified"
|
||||
// I see this column defined when, on a device using SAF we select a file using the
|
||||
// file browser
|
||||
// According to https://developer.android.com/reference/kotlin/android/provider/DocumentsContract
|
||||
// all "document providers" must provide certain columns. Do we actually have a DocumentProvider here?
|
||||
// I do not think this code path will ever happen for a non-media file is selected on
|
||||
// an API < KitKat device, since those will be delivered as a file:// URI and handled
|
||||
// accordingly. Therefore, it is safe to ignore the warning that this field requires
|
||||
// API 19
|
||||
@SuppressLint("InlinedApi")
|
||||
int documentLastModifiedColumnIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED);
|
||||
|
||||
// If we have an image, it may be the case that MediaStore.MediaColumns.DATE_MODIFIED
|
||||
// catches the modification date, but if not, here is another column we can look for.
|
||||
// This should be checked *after* DATE_MODIFIED since I think that column might give
|
||||
// better information
|
||||
int imageDateTakenColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
|
||||
|
||||
// Report whether the captured timestamp is in milliseconds or seconds
|
||||
// The truthy-ness of this value for each different type of column is known from either
|
||||
// experimentation or the docs (when docs exist...)
|
||||
boolean milliseconds;
|
||||
|
||||
int properColumnIndex;
|
||||
if (mediaDataModifiedColumnIndex >= 0) {
|
||||
properColumnIndex = mediaDataModifiedColumnIndex;
|
||||
milliseconds = false;
|
||||
} else if (documentLastModifiedColumnIndex >= 0) {
|
||||
properColumnIndex = documentLastModifiedColumnIndex;
|
||||
milliseconds = true;
|
||||
} else if (imageDateTakenColumnIndex >= 0) {
|
||||
properColumnIndex = imageDateTakenColumnIndex;
|
||||
milliseconds = true;
|
||||
} else {
|
||||
// Nothing worked :(
|
||||
String formattedColumns = Arrays.toString(allColumns);
|
||||
Log.w("SendFileActivity", "Unable to get file modification time. Available columns were: " + formattedColumns);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!cursor.isNull(properColumnIndex)) {
|
||||
lastModifiedTime = cursor.getLong(properColumnIndex);
|
||||
}
|
||||
|
||||
if (!milliseconds) {
|
||||
lastModifiedTime *= 1000;
|
||||
milliseconds = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastModifiedTime;
|
||||
}
|
||||
}
|
206
src/org/kde/kdeconnect/Helpers/FilesHelper.kt
Normal file
206
src/org/kde/kdeconnect/Helpers/FilesHelper.kt
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import android.webkit.MimeTypeMap
|
||||
import org.apache.commons.io.FilenameUtils
|
||||
import org.kde.kdeconnect.NetworkPacket
|
||||
import java.io.File
|
||||
import kotlin.math.min
|
||||
|
||||
object FilesHelper {
|
||||
private const val LOG_TAG: String = "SendFileActivity"
|
||||
|
||||
@JvmStatic
|
||||
fun getMimeTypeFromFile(file: String?): String {
|
||||
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(FilenameUtils.getExtension(file))
|
||||
return mime ?: "*/*"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findNonExistingNameForNewFile(path: String, filename: String): String {
|
||||
var newFilename = filename
|
||||
val name = FilenameUtils.getBaseName(newFilename)
|
||||
val ext = FilenameUtils.getExtension(newFilename)
|
||||
|
||||
var num = 1
|
||||
while (File("$path/$newFilename").exists()) {
|
||||
newFilename = "$name ($num).$ext"
|
||||
num++
|
||||
}
|
||||
|
||||
return newFilename
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts any string into a string that is safe to use as a file name.
|
||||
* The result will only include ascii characters and numbers, and the "-","_", and "." characters.
|
||||
*/
|
||||
private fun toFileSystemSafeName(name: String, dirSeparators: Boolean, maxFileLength: Int): String {
|
||||
fun isSafeChar(c: Char): Boolean =
|
||||
c in 'a'..'z' || c in 'A'..'Z' || c in '0'..'9' ||
|
||||
c == '_' || c == '-' || c == '.' ||
|
||||
(dirSeparators && ((c == '/') || (c == '\\')))
|
||||
|
||||
val nameSafeChars = name.filter(::isSafeChar)
|
||||
val nameSafeLength = nameSafeChars.substring(nameSafeChars.length - min(nameSafeChars.length, maxFileLength))
|
||||
return nameSafeLength
|
||||
}
|
||||
fun toFileSystemSafeName(name: String, dirSeparators: Boolean): String = toFileSystemSafeName(name, dirSeparators, 255)
|
||||
fun toFileSystemSafeName(name: String): String = toFileSystemSafeName(name, true, 255)
|
||||
|
||||
private fun getOpenFileCount(): Int? = File("/proc/self/fd").listFiles()?.size
|
||||
|
||||
fun LogOpenFileCount() {
|
||||
Log.e("KDE/FileCount", "" + getOpenFileCount())
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a network packet from the given URI
|
||||
*/
|
||||
@JvmStatic
|
||||
fun uriToNetworkPacket(context: Context, uri: Uri, type: String?): NetworkPacket? {
|
||||
try {
|
||||
val contentResolver = context.contentResolver
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
|
||||
val packet = NetworkPacket(type!!)
|
||||
|
||||
val sizeDefault = -1L
|
||||
|
||||
// file:// is a non media uri, so we cannot query the ContentProvider
|
||||
fun fileSchemeExtract(): Triple<String?, Long, Long?> {
|
||||
val path = uri.path
|
||||
if (path != null) {
|
||||
val mFile = File(path)
|
||||
|
||||
val filename = mFile.name
|
||||
val size = mFile.length()
|
||||
val lastModified = mFile.lastModified()
|
||||
return Triple(filename, size, lastModified)
|
||||
}
|
||||
else {
|
||||
Log.e(LOG_TAG, "Received bad file URI, path was null")
|
||||
return Triple(null, sizeDefault, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun contentResolverExtract(): Triple<String?, Long, Long?> {
|
||||
// Since we used Intent.CATEGORY_OPENABLE, these two columns are the only ones we are guaranteed to have: https://developer.android.com/reference/android/provider/OpenableColumns
|
||||
val proj = arrayOf(OpenableColumns.SIZE, OpenableColumns.DISPLAY_NAME,)
|
||||
|
||||
try {
|
||||
contentResolver.query(uri, proj, null, null, null).use { cursor ->
|
||||
val nameColumnIndex = cursor!!.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
|
||||
val sizeColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE)
|
||||
cursor.moveToFirst()
|
||||
|
||||
val filename = cursor.getString(nameColumnIndex)
|
||||
// It is recommended to check for the value to be null because there are situations were we don't know the size (for instance, if the file is not local to the device)
|
||||
val size = if (!cursor.isNull(sizeColumnIndex)) cursor.getLong(sizeColumnIndex) else sizeDefault
|
||||
val lastModified = getLastModifiedTime(context, uri)
|
||||
return Triple(filename, size, lastModified)
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "Problem getting file information", e)
|
||||
}
|
||||
return Triple(null, sizeDefault, null)
|
||||
}
|
||||
|
||||
val (filename, size, lastModified) = when (uri.scheme) {
|
||||
"file" -> fileSchemeExtract()
|
||||
else -> contentResolverExtract()
|
||||
}
|
||||
|
||||
if (filename != null) {
|
||||
packet["filename"] = filename
|
||||
}
|
||||
else {
|
||||
// It would be very surprising if this happens
|
||||
Log.e(LOG_TAG, "Unable to read filename")
|
||||
}
|
||||
|
||||
if (lastModified != null) {
|
||||
packet["lastModified"] = lastModified
|
||||
}
|
||||
else {
|
||||
// This would not be too surprising, and probably means we need to improve FilesHelper.getLastModifiedTime
|
||||
Log.w(LOG_TAG, "Unable to read file last modified time")
|
||||
}
|
||||
|
||||
packet.payload = NetworkPacket.Payload(inputStream, size)
|
||||
|
||||
return packet
|
||||
}
|
||||
catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "Exception creating network packet", e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By hook or by crook, get the last modified time of the passed content:// URI
|
||||
*
|
||||
* This is a challenge because different content sources have different columns defined, and
|
||||
* I don't know how to tell what the source of the content is.
|
||||
*
|
||||
* Therefore, my brilliant solution is to just try everything until something works.
|
||||
*
|
||||
* Will return null if nothing worked.
|
||||
*
|
||||
* @return Time last modified in milliseconds or null
|
||||
*/
|
||||
fun getLastModifiedTime(context: Context, uri: Uri): Long? {
|
||||
context.contentResolver.query(uri, null, null, null, null).use { cursor ->
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val allColumns = cursor.columnNames
|
||||
|
||||
// MediaStore.MediaColumns.DATE_MODIFIED resolves to "date_modified"
|
||||
// I see this column defined in case we used the Gallery app to select the file to transfer
|
||||
// This can occur both for devices running Storage Access Framework (SAF) if we select the Gallery to provide the file to transfer, as well as for older devices by doing the same
|
||||
val mediaDataModifiedColumnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED)
|
||||
|
||||
// DocumentsContract.Document.COLUMN_LAST_MODIFIED resolves to "last_modified"
|
||||
// I see this column defined when, on a device using SAF we select a file using the file browser
|
||||
// According to https://developer.android.com/reference/kotlin/android/provider/DocumentsContract all "document providers" must provide certain columns.
|
||||
// Do we actually have a DocumentProvider here?
|
||||
// I do not think this code path will ever happen for a non-media file is selected on an API < KitKat device, since those will be delivered as a file:// URI and handled accordingly.
|
||||
// Therefore, it is safe to ignore the warning that this field requires API 19
|
||||
@SuppressLint("InlinedApi") val documentLastModifiedColumnIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED)
|
||||
|
||||
// If we have an image, it may be the case that MediaStore.MediaColumns.DATE_MODIFIED catches the modification date, but if not, here is another column we can look for.
|
||||
// This should be checked *after* DATE_MODIFIED since I think that column might give better information
|
||||
val imageDateTakenColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN)
|
||||
|
||||
// Report whether the captured timestamp is in milliseconds or seconds
|
||||
// The truthy-ness of this value for each different type of column is known from either experimentation or the docs (when docs exist...)
|
||||
val (properColumnIndex: Int, milliseconds: Boolean) = when {
|
||||
mediaDataModifiedColumnIndex >= 0 -> Pair(mediaDataModifiedColumnIndex, false)
|
||||
documentLastModifiedColumnIndex >= 0 -> Pair(documentLastModifiedColumnIndex, true)
|
||||
imageDateTakenColumnIndex >= 0 -> Pair(imageDateTakenColumnIndex, true)
|
||||
else -> {
|
||||
// Nothing worked :(
|
||||
Log.w("SendFileActivity", "Unable to get file modification time. Available columns were: ${allColumns.contentToString()}")
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
val lastModifiedTime: Long? = if (!cursor.isNull(properColumnIndex)) cursor.getLong(properColumnIndex) else null
|
||||
|
||||
return if (!milliseconds) lastModifiedTime!! * 1000 else lastModifiedTime
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
@@ -28,7 +28,7 @@ public class IntentHelper {
|
||||
*/
|
||||
public static void startActivityFromBackgroundOrCreateNotification(Context context, Intent intent, String title) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !LifecycleHelper.isInForeground()) {
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
|
||||
Notification notification = new NotificationCompat
|
||||
.Builder(context, NotificationHelper.Channels.HIGHPRIORITY)
|
||||
.setContentIntent(pendingIntent)
|
||||
|
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Erik Duisters <e.duisters1@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
public class LifecycleHelper {
|
||||
|
||||
private static class LifecycleObserver implements DefaultLifecycleObserver {
|
||||
private boolean inForeground = false;
|
||||
|
||||
@Override
|
||||
public void onStart(@NonNull LifecycleOwner owner) {
|
||||
inForeground = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@NonNull LifecycleOwner owner) {
|
||||
inForeground = false;
|
||||
}
|
||||
|
||||
boolean isInForeground() {
|
||||
return inForeground;
|
||||
}
|
||||
}
|
||||
|
||||
private final static LifecycleObserver foregroundTracker = new LifecycleObserver();
|
||||
|
||||
public static boolean isInForeground() {
|
||||
return foregroundTracker.isInForeground();
|
||||
}
|
||||
|
||||
public static void initializeObserver() {
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(foregroundTracker);
|
||||
}
|
||||
}
|
33
src/org/kde/kdeconnect/Helpers/LifecycleHelper.kt
Normal file
33
src/org/kde/kdeconnect/Helpers/LifecycleHelper.kt
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Erik Duisters <e.duisters1@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
|
||||
object LifecycleHelper {
|
||||
fun initializeObserver() {
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(foregroundTracker)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val isInForeground: Boolean
|
||||
get() = foregroundTracker.isInForeground
|
||||
|
||||
private val foregroundTracker = object : DefaultLifecycleObserver {
|
||||
var isInForeground: Boolean = false
|
||||
private set
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
this.isInForeground = true
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
this.isInForeground = false
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
public class MediaStoreHelper {
|
||||
|
||||
//Maybe this class could batch successive calls together
|
||||
|
||||
public static void indexFile(Context context, Uri path) {
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(path);
|
||||
context.sendBroadcast(mediaScanIntent);
|
||||
}
|
||||
|
||||
}
|
20
src/org/kde/kdeconnect/Helpers/MediaStoreHelper.kt
Normal file
20
src/org/kde/kdeconnect/Helpers/MediaStoreHelper.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
|
||||
object MediaStoreHelper {
|
||||
// Maybe this class could batch successive calls together
|
||||
@JvmStatic
|
||||
fun indexFile(context: Context, path: Uri?) {
|
||||
val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
|
||||
mediaScanIntent.setData(path)
|
||||
context.sendBroadcast(mediaScanIntent)
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class RandomHelper {
|
||||
public static final SecureRandom secureRandom = new SecureRandom();
|
||||
|
||||
private static final char[] symbols = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"abcdefghijklmnopqrstuvwxyz" +
|
||||
"1234567890").toCharArray();
|
||||
|
||||
|
||||
public static String randomString(int length) {
|
||||
char[] buffer = new char[length];
|
||||
for (int idx = 0; idx < length; ++idx) {
|
||||
buffer[idx] = symbols[secureRandom.nextInt(symbols.length)];
|
||||
}
|
||||
return new String(buffer);
|
||||
}
|
||||
|
||||
}
|
22
src/org/kde/kdeconnect/Helpers/RandomHelper.kt
Normal file
22
src/org/kde/kdeconnect/Helpers/RandomHelper.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import java.security.SecureRandom
|
||||
|
||||
object RandomHelper {
|
||||
@JvmField
|
||||
val secureRandom: SecureRandom = SecureRandom()
|
||||
|
||||
private val symbols = (('A'..'Z').toList() + ('a'..'z').toList() + ('0'..'9').toList()).toCharArray()
|
||||
fun randomString(length: Int): String {
|
||||
val buffer = CharArray(length)
|
||||
for (i in 0 until length) {
|
||||
buffer[i] = symbols[secureRandom.nextInt(symbols.size)]
|
||||
}
|
||||
return String(buffer)
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Daniel Weigl <DanielWeigl@gmx.at>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
public class SafeTextChecker {
|
||||
|
||||
private final String safeChars;
|
||||
private final Integer maxLength;
|
||||
|
||||
public SafeTextChecker(String safeChars, Integer maxLength) {
|
||||
this.safeChars = safeChars;
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
|
||||
// is used by the SendKeystrokes functionality to evaluate if a to-be-send text is safe for
|
||||
// sending without user confirmation
|
||||
// only allow sending text that can not harm any connected desktop (like "format c:\n" / "rm -rf\n",...)
|
||||
public boolean isSafe(String content) {
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (content.length() > maxLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < content.length(); i++) {
|
||||
String charAtPos = content.substring(i, i + 1);
|
||||
if (!safeChars.contains(charAtPos)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// we are happy with the string
|
||||
return true;
|
||||
}
|
||||
}
|
25
src/org/kde/kdeconnect/Helpers/SafeTextChecker.kt
Normal file
25
src/org/kde/kdeconnect/Helpers/SafeTextChecker.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Daniel Weigl <DanielWeigl@gmx.at>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
class SafeTextChecker {
|
||||
private val safeChars: String
|
||||
private val maxLength: Int
|
||||
|
||||
constructor(safeChars: String, maxLength: Int) {
|
||||
this.safeChars = safeChars
|
||||
this.maxLength = maxLength
|
||||
}
|
||||
|
||||
// is used by the SendKeystrokes functionality to evaluate if a to-be-send text is safe for
|
||||
// sending without user confirmation
|
||||
// only allow sending text that can not harm any connected desktop (like "format c:\n" / "rm -rf\n",...)
|
||||
fun isSafe(content: String?): Boolean {
|
||||
return content != null &&
|
||||
content.length <= maxLength &&
|
||||
content.toCharArray().all { c -> c in safeChars }
|
||||
}
|
||||
}
|
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers.SecurityHelpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.security.keystore.KeyProperties;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.ECGenParameterSpec;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class RsaHelper {
|
||||
|
||||
public static void initialiseRsaKeys(Context context) {
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
if (!settings.contains("publicKey") || !settings.contains("privateKey")) {
|
||||
|
||||
KeyPair keyPair;
|
||||
String keyAlgorithm;
|
||||
try {
|
||||
KeyPairGenerator keyGen;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
keyAlgorithm = KeyProperties.KEY_ALGORITHM_EC;
|
||||
keyGen = KeyPairGenerator.getInstance(keyAlgorithm);
|
||||
ECGenParameterSpec spec = new ECGenParameterSpec("secp256r1");
|
||||
keyGen.initialize(spec);
|
||||
} else {
|
||||
keyAlgorithm = "RSA";
|
||||
keyGen = KeyPairGenerator.getInstance(keyAlgorithm);
|
||||
keyGen.initialize(2048);
|
||||
}
|
||||
keyPair = keyGen.generateKeyPair();
|
||||
} catch (Exception e) {
|
||||
Log.e("KDE/initializeRsaKeys", "Exception", e);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
byte[] privateKey = keyPair.getPrivate().getEncoded();
|
||||
|
||||
SharedPreferences.Editor edit = settings.edit();
|
||||
edit.putString("publicKey", Base64.encodeToString(publicKey, 0).trim() + "\n");
|
||||
edit.putString("privateKey", Base64.encodeToString(privateKey, 0));
|
||||
edit.putString("keyAlgorithm", keyAlgorithm);
|
||||
edit.apply();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static PublicKey getPublicKey(Context context) throws GeneralSecurityException {
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
|
||||
// For backwards compat: if no keyAlgorithm setting is set, it means it was generated using RSA
|
||||
String keyAlgorithm = settings.getString("keyAlgorithm", "RSA");
|
||||
return KeyFactory.getInstance(keyAlgorithm).generatePublic(new X509EncodedKeySpec(publicKeyBytes));
|
||||
}
|
||||
|
||||
public static PrivateKey getPrivateKey(Context context) throws GeneralSecurityException {
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
byte[] privateKeyBytes = Base64.decode(settings.getString("privateKey", ""), 0);
|
||||
// For backwards compat: if no keyAlgorithm setting is set, it means it was generated using RSA
|
||||
String keyAlgorithm = settings.getString("keyAlgorithm", "RSA");
|
||||
return KeyFactory.getInstance(keyAlgorithm).generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
83
src/org/kde/kdeconnect/Helpers/SecurityHelpers/RsaHelper.kt
Normal file
83
src/org/kde/kdeconnect/Helpers/SecurityHelpers/RsaHelper.kt
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers.SecurityHelpers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.preference.PreferenceManager
|
||||
import android.security.keystore.KeyProperties
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import java.security.GeneralSecurityException
|
||||
import java.security.KeyFactory
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.spec.ECGenParameterSpec
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
|
||||
object RsaHelper {
|
||||
private const val RSA = "RSA" // KeyProperties.KEY_ALGORITHM_RSA isn't available until API 23+
|
||||
|
||||
@JvmStatic
|
||||
fun initialiseRsaKeys(context: Context?) {
|
||||
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
if (!settings.contains("publicKey") || !settings.contains("privateKey")) {
|
||||
val keyPair: KeyPair
|
||||
val keyAlgorithm: String
|
||||
try {
|
||||
val newSdk = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
keyAlgorithm = if (newSdk) KeyProperties.KEY_ALGORITHM_EC else RSA
|
||||
val generator = KeyPairGenerator.getInstance(keyAlgorithm)
|
||||
if (newSdk) {
|
||||
val spec = ECGenParameterSpec("secp256r1")
|
||||
generator.initialize(spec)
|
||||
}
|
||||
else {
|
||||
generator.initialize(2048)
|
||||
}
|
||||
keyPair = generator.generateKeyPair()
|
||||
}
|
||||
catch (e: Exception) {
|
||||
Log.e("KDE/initializeRsaKeys", "Exception", e)
|
||||
return
|
||||
}
|
||||
|
||||
val publicKey = keyPair.public.encoded
|
||||
val privateKey = keyPair.private.encoded
|
||||
|
||||
settings.edit().apply {
|
||||
putString("publicKey", Base64.encodeToString(publicKey, 0))
|
||||
putString("privateKey", Base64.encodeToString(privateKey, 0))
|
||||
putString("keyAlgorithm", keyAlgorithm)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** For backwards compat: if no keyAlgorithm setting is set, it means it was generated using RSA */
|
||||
private fun algorithmFromSettings(pref: SharedPreferences) = pref.getString("keyAlgorithm", RSA)
|
||||
|
||||
@JvmStatic
|
||||
@Throws(GeneralSecurityException::class)
|
||||
fun getPublicKey(context: Context?): PublicKey {
|
||||
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0)
|
||||
return KeyFactory.getInstance(algorithmFromSettings(settings)).generatePublic(X509EncodedKeySpec(publicKeyBytes))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Throws(GeneralSecurityException::class)
|
||||
fun getPrivateKey(context: Context?): PrivateKey {
|
||||
val settings = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val privateKeyBytes = Base64.decode(settings.getString("privateKey", ""), 0)
|
||||
return KeyFactory.getInstance(algorithmFromSettings(settings)).generatePrivate(PKCS8EncodedKeySpec(privateKeyBytes))
|
||||
}
|
||||
}
|
@@ -26,6 +26,7 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.RandomHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -104,7 +105,9 @@ public class SslHelper {
|
||||
|
||||
boolean needsToGenerateCertificate = false;
|
||||
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
if (settings.contains("certificate")) {
|
||||
final Date currDate = new Date();
|
||||
try {
|
||||
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
byte[] certificateBytes = Base64.decode(globalSettings.getString("certificate", ""), 0);
|
||||
@@ -114,7 +117,14 @@ public class SslHelper {
|
||||
if (!certDeviceId.equals(deviceId)) {
|
||||
Log.e("KDE/SslHelper", "The certificate stored is from a different device id! (found: " + certDeviceId + " expected:" + deviceId + ")");
|
||||
needsToGenerateCertificate = true;
|
||||
} else {
|
||||
}
|
||||
else if(cert.getNotAfter().getTime() < currDate.getTime()) {
|
||||
Log.e("KDE/SslHelper", "The certificate expired: "+cert.getNotAfter());
|
||||
needsToGenerateCertificate = true;
|
||||
} else if(cert.getNotBefore().getTime() > currDate.getTime()) {
|
||||
Log.e("KDE/SslHelper", "The certificate is not effective yet: "+cert.getNotBefore());
|
||||
needsToGenerateCertificate = true;
|
||||
}else {
|
||||
certificate = cert;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -127,6 +137,7 @@ public class SslHelper {
|
||||
}
|
||||
|
||||
if (needsToGenerateCertificate) {
|
||||
KdeConnect.getInstance().removeRememberedDevices();
|
||||
Log.i("KDE/SslHelper", "Generating a certificate");
|
||||
try {
|
||||
//Fix for https://issuetracker.google.com/issues/37095309
|
||||
@@ -137,10 +148,9 @@ public class SslHelper {
|
||||
nameBuilder.addRDN(BCStyle.CN, deviceId);
|
||||
nameBuilder.addRDN(BCStyle.OU, "KDE Connect");
|
||||
nameBuilder.addRDN(BCStyle.O, "KDE");
|
||||
final LocalDate localDate = LocalDate.now().minusYears(1);
|
||||
final Instant notBefore = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||
final Instant notAfter = localDate.plusYears(10).atStartOfDay(ZoneId.systemDefault())
|
||||
.toInstant();
|
||||
final LocalDate localDate = LocalDate.now();
|
||||
final Instant notBefore = localDate.minusYears(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||
final Instant notAfter = localDate.plusYears(10).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||
X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
|
||||
nameBuilder.build(),
|
||||
BigInteger.ONE,
|
||||
|
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//Code from http://stackoverflow.com/questions/9340332/how-can-i-get-the-list-of-mounted-external-storage-of-android-device/19982338#19982338
|
||||
//modified to work on Lollipop and other devices
|
||||
public class StorageHelper {
|
||||
|
||||
public static class StorageInfo {
|
||||
|
||||
public final String path;
|
||||
public final boolean readonly;
|
||||
public final boolean removable;
|
||||
public final int number;
|
||||
|
||||
public StorageInfo(String path, boolean readonly, boolean removable, int number) {
|
||||
this.path = path;
|
||||
this.readonly = readonly;
|
||||
this.removable = removable;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is bullshit because there is no proper way to do this in Android.
|
||||
* Patch after patch I'm making it even more horrible by trying to make it work for *more*
|
||||
* devices while trying no to break previously working ones.
|
||||
* If this function was a living being, it would be begging "please kill me".
|
||||
*/
|
||||
public static List<StorageInfo> getStorageList() {
|
||||
|
||||
List<StorageInfo> list = new ArrayList<>();
|
||||
String def_path = Environment.getExternalStorageDirectory().getPath();
|
||||
boolean def_path_removable = Environment.isExternalStorageRemovable();
|
||||
String def_path_state = Environment.getExternalStorageState();
|
||||
boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
|
||||
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
|
||||
boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
|
||||
|
||||
HashSet<String> paths = new HashSet<>();
|
||||
int cur_removable_number = 1;
|
||||
|
||||
if (def_path_available) {
|
||||
paths.add(def_path);
|
||||
list.add(0, new StorageInfo(def_path, def_path_readonly, def_path_removable, def_path_removable ? cur_removable_number++ : -1));
|
||||
}
|
||||
|
||||
File storage = new File("/storage/");
|
||||
if (storage.exists() && storage.isDirectory() && storage.canRead()) {
|
||||
String mounts = null;
|
||||
try (Scanner scanner = new Scanner(new File("/proc/mounts"))) {
|
||||
mounts = scanner.useDelimiter("\\A").next();
|
||||
} catch (Exception e) {
|
||||
Log.e("StorageHelper", "Exception while getting storageList", e);
|
||||
}
|
||||
|
||||
File[] dirs = storage.listFiles();
|
||||
for (File dir : dirs) {
|
||||
//Log.e("getStorageList", "path: "+dir.getAbsolutePath());
|
||||
if (dir.isDirectory() && dir.canRead() && dir.canExecute()) {
|
||||
String path, path2;
|
||||
path2 = dir.getAbsolutePath();
|
||||
try {
|
||||
//Log.e(dir.getAbsolutePath(), dir.getCanonicalPath());
|
||||
path = dir.getCanonicalPath();
|
||||
} catch (Exception e) {
|
||||
path = path2;
|
||||
}
|
||||
if (!path.startsWith("/storage/emulated") || dirs.length == 1) {
|
||||
if (!paths.contains(path) && !paths.contains(path2)) {
|
||||
if (mounts == null || StringUtils.containsAny(mounts, path, path2)) {
|
||||
list.add(0, new StorageInfo(path, dir.canWrite(), true, cur_removable_number++));
|
||||
paths.add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Legacy code for Android < 4.0 that still didn't have /storage
|
||||
List<String> entries = new ArrayList<>();
|
||||
try (FileReader fileReader = new FileReader("/proc/mounts")) {
|
||||
// The reader is buffered internally, so buffering it separately is unnecessary.
|
||||
final List<String> lines = IOUtils.readLines(fileReader).stream()
|
||||
.filter(line -> StringUtils.containsAny(line, "vfat", "exfat", "ntfs", "/mnt"))
|
||||
.collect(Collectors.toList());
|
||||
for (String line : lines) {
|
||||
if (line.contains("/storage/sdcard"))
|
||||
entries.add(0, line);
|
||||
else
|
||||
entries.add(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("StorageHelper", "Exception", e);
|
||||
}
|
||||
|
||||
for (String line : entries) {
|
||||
StringTokenizer tokens = new StringTokenizer(line, " ");
|
||||
tokens.nextToken(); //device
|
||||
String mount_point = tokens.nextToken(); //mount point
|
||||
if (paths.contains(mount_point)) {
|
||||
continue;
|
||||
}
|
||||
tokens.nextToken(); //file system
|
||||
String[] flags = tokens.nextToken().split(","); //flags
|
||||
boolean readonly = ArrayUtils.contains(flags, "ro");
|
||||
|
||||
if (line.contains("/dev/block/vold") && !StringUtils.containsAny(line, "/mnt/secure",
|
||||
"/mnt/asec", "/mnt/obb", "/dev/mapper", "tmpfs")) {
|
||||
paths.add(mount_point);
|
||||
list.add(new StorageInfo(mount_point, readonly, true, cur_removable_number++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* treeUri documentId
|
||||
* ==================================================================================================
|
||||
* content://com.android.providers.downloads.documents/tree/downloads => downloads
|
||||
* content://com.android.externalstorage.documents/tree/1715-1D1F: => 1715-1D1F:
|
||||
* content://com.android.externalstorage.documents/tree/1715-1D1F:My%20Photos => 1715-1D1F:My Photos
|
||||
* content://com.android.externalstorage.documents/tree/primary: => primary:
|
||||
* content://com.android.externalstorage.documents/tree/primary:DCIM => primary:DCIM
|
||||
* content://com.android.externalstorage.documents/tree/primary:Download/bla => primary:Download/bla
|
||||
*/
|
||||
public static String getDisplayName(@NonNull Context context, @NonNull Uri treeUri) {
|
||||
List<String> pathSegments = treeUri.getPathSegments();
|
||||
|
||||
if (!pathSegments.get(0).equals("tree")) {
|
||||
throw new IllegalArgumentException("treeUri is not valid");
|
||||
}
|
||||
|
||||
String documentId = DocumentsContract.getTreeDocumentId(treeUri);
|
||||
|
||||
int colonIdx = pathSegments.get(1).indexOf(':');
|
||||
|
||||
if (colonIdx >= 0) {
|
||||
String tree = pathSegments.get(1).substring(0, colonIdx + 1);
|
||||
|
||||
if (!documentId.equals(tree)) {
|
||||
return documentId.substring(tree.length());
|
||||
} else {
|
||||
return documentId.substring(0, colonIdx);
|
||||
}
|
||||
}
|
||||
|
||||
return documentId;
|
||||
}
|
||||
}
|
19
src/org/kde/kdeconnect/Helpers/StorageHelper.kt
Normal file
19
src/org/kde/kdeconnect/Helpers/StorageHelper.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 TPJ Schikhof <kde@schikhof.eu>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
|
||||
object StorageHelper {
|
||||
fun getDisplayName(treeUri: Uri): String {
|
||||
val pathSegments = treeUri.pathSegments
|
||||
require(pathSegments[0] == "tree") { "treeUri is not valid" }
|
||||
|
||||
val segmentsWithoutTree = pathSegments.drop(1).joinToString("/")
|
||||
return segmentsWithoutTree.split(":").last(String::isNotBlank)
|
||||
}
|
||||
}
|
@@ -310,7 +310,7 @@ public class TelephonyHelper {
|
||||
* @param requestType Value which we would like to find in types
|
||||
* @return True if the APN supports the requested type, false otherwise
|
||||
*/
|
||||
private static boolean isValidApnType(String types, String requestType) {
|
||||
public static boolean isValidApnType(String types, String requestType) {
|
||||
// If APN type is unspecified, assume APN_TYPE_ALL.
|
||||
if (types.isEmpty()) {
|
||||
return true;
|
||||
|
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019 Juan David Vega <jdvr.93@hotmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.wifi.SupplicantState;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TrustedNetworkHelper {
|
||||
|
||||
private static final String KEY_CUSTOM_TRUSTED_NETWORKS = "trusted_network_preference";
|
||||
private static final String KEY_CUSTOM_TRUST_ALL_NETWORKS = "trust_all_network_preference";
|
||||
private static final String NETWORK_SSID_DELIMITER = "#_#";
|
||||
private static final String NOT_AVAILABLE_SSID_RESULT = "<unknown ssid>";
|
||||
|
||||
|
||||
private final Context context;
|
||||
|
||||
public TrustedNetworkHelper(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public String[] read() {
|
||||
String serializeTrustedNetwork = PreferenceManager.getDefaultSharedPreferences(context).getString(
|
||||
KEY_CUSTOM_TRUSTED_NETWORKS, "");
|
||||
if (serializeTrustedNetwork.isEmpty())
|
||||
return ArrayUtils.EMPTY_STRING_ARRAY;
|
||||
return serializeTrustedNetwork.split(NETWORK_SSID_DELIMITER);
|
||||
}
|
||||
|
||||
public void update(List<String> trustedNetworks) {
|
||||
String serialized = TextUtils.join(NETWORK_SSID_DELIMITER, trustedNetworks);
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
|
||||
KEY_CUSTOM_TRUSTED_NETWORKS, serialized).apply();
|
||||
}
|
||||
|
||||
public boolean allAllowed() {
|
||||
if (!hasPermissions()) {
|
||||
return true;
|
||||
}
|
||||
return PreferenceManager
|
||||
.getDefaultSharedPreferences(context)
|
||||
.getBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, Boolean.TRUE);
|
||||
}
|
||||
|
||||
public void allAllowed(boolean isChecked) {
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, isChecked)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public boolean hasPermissions() {
|
||||
int result = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
return (result == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
||||
public String currentSSID() {
|
||||
WifiManager wifiManager = ContextCompat.getSystemService(context.getApplicationContext(),
|
||||
WifiManager.class);
|
||||
if (wifiManager == null) return "";
|
||||
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
|
||||
if (wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) {
|
||||
return "";
|
||||
}
|
||||
String ssid = wifiInfo.getSSID();
|
||||
if (ssid.equalsIgnoreCase(NOT_AVAILABLE_SSID_RESULT)){
|
||||
Log.d("TrustedNetworkHelper", "Current SSID is unknown");
|
||||
return "";
|
||||
}
|
||||
return ssid;
|
||||
}
|
||||
|
||||
public static boolean isTrustedNetwork(Context context) {
|
||||
TrustedNetworkHelper trustedNetworkHelper = new TrustedNetworkHelper(context);
|
||||
if (trustedNetworkHelper.allAllowed()){
|
||||
return true;
|
||||
}
|
||||
return ArrayUtils.contains(trustedNetworkHelper.read(), trustedNetworkHelper.currentSSID());
|
||||
}
|
||||
}
|
71
src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.kt
Normal file
71
src/org/kde/kdeconnect/Helpers/TrustedNetworkHelper.kt
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 TPJ Schikhof <kde@schikhof.eu>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.wifi.SupplicantState
|
||||
import android.net.wifi.WifiManager
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
class TrustedNetworkHelper(private val context: Context) {
|
||||
|
||||
var trustedNetworks: List<String>
|
||||
get() {
|
||||
val serializedNetworks = PreferenceManager.getDefaultSharedPreferences(context).getString(KEY_CUSTOM_TRUSTED_NETWORKS, "") ?: ""
|
||||
return serializedNetworks.split(NETWORK_SSID_DELIMITER, "#_#" /* TODO remove old delimiter in 2025 */).filter { it.isNotEmpty() }
|
||||
}
|
||||
set(value) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putString(KEY_CUSTOM_TRUSTED_NETWORKS, value.joinToString(NETWORK_SSID_DELIMITER))
|
||||
.apply()
|
||||
}
|
||||
|
||||
var allNetworksAllowed: Boolean
|
||||
get() = !hasPermissions || PreferenceManager.getDefaultSharedPreferences(context).getBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, true)
|
||||
set(value) = PreferenceManager
|
||||
.getDefaultSharedPreferences(context)
|
||||
.edit()
|
||||
.putBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, value)
|
||||
.apply()
|
||||
|
||||
val hasPermissions: Boolean
|
||||
get() = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
/** @return The current SSID or null if it's not available for any reason */
|
||||
val currentSSID: String?
|
||||
get() {
|
||||
val wifiManager = ContextCompat.getSystemService(context.applicationContext, WifiManager::class.java) ?: return null
|
||||
val wifiInfo = wifiManager.connectionInfo
|
||||
if (wifiInfo.supplicantState != SupplicantState.COMPLETED) return null
|
||||
val ssid = wifiInfo.ssid
|
||||
return when {
|
||||
ssid.equals(NOT_AVAILABLE_SSID_RESULT, ignoreCase = true) -> {
|
||||
Log.d("TrustedNetworkHelper", "Current SSID is unknown")
|
||||
null
|
||||
}
|
||||
ssid.isBlank() -> null
|
||||
else -> ssid
|
||||
}
|
||||
}
|
||||
|
||||
val isTrustedNetwork: Boolean
|
||||
get() = this.allNetworksAllowed || this.currentSSID in this.trustedNetworks
|
||||
|
||||
companion object {
|
||||
private const val KEY_CUSTOM_TRUSTED_NETWORKS = "trusted_network_preference"
|
||||
private const val KEY_CUSTOM_TRUST_ALL_NETWORKS = "trust_all_network_preference"
|
||||
private const val NETWORK_SSID_DELIMITER = "\u0000"
|
||||
private const val NOT_AVAILABLE_SSID_RESULT = "<unknown ssid>"
|
||||
|
||||
@JvmStatic
|
||||
fun isTrustedNetwork(context: Context): Boolean = TrustedNetworkHelper(context).isTrustedNetwork
|
||||
}
|
||||
}
|
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Soul Trace <S-trace@list.ru>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
public class VideoUrlsHelper {
|
||||
private static final int SECONDS_IN_MINUTE = 60;
|
||||
private static final int MINUTES_IN_HOUR = 60;
|
||||
private static final int SECONDS_IN_HOUR = SECONDS_IN_MINUTE * MINUTES_IN_HOUR;
|
||||
|
||||
public static URL formatUriWithSeek(String address, long position)
|
||||
throws MalformedURLException {
|
||||
URL url = new URL(address);
|
||||
position /= 1000; // Convert ms to seconds
|
||||
if (position <= 0) {
|
||||
return url; // nothing to do
|
||||
}
|
||||
String host = url.getHost().toLowerCase();
|
||||
|
||||
// Most common settings as defaults:
|
||||
String parameter = "t="; // Characters before timestamp
|
||||
String timestamp = Long.toString(position); // Timestamp itself
|
||||
String trailer = ""; // Characters after timestamp
|
||||
// true - search/add to query URL part (between ? and # signs),
|
||||
// false - search/add timestamp to ref (anchor) URL part (after # sign),
|
||||
boolean inQuery = true;
|
||||
// true - We know how to format URL with seek timestamp, false - not
|
||||
boolean seekUrl = false;
|
||||
|
||||
// Override defaults if necessary
|
||||
if (StringUtils.containsAny(host, "youtube.com", "youtu.be", "pornhub.com")) {
|
||||
seekUrl = true;
|
||||
url = stripTimestampS(url, parameter, trailer, inQuery);
|
||||
} else if (host.contains("vimeo.com")) {
|
||||
seekUrl = true;
|
||||
trailer = "s";
|
||||
url = stripTimestampS(url, parameter, trailer, inQuery);
|
||||
} else if (host.contains("dailymotion.com")) {
|
||||
seekUrl = true;
|
||||
parameter = "start=";
|
||||
url = stripTimestampS(url, parameter, trailer, inQuery);
|
||||
} else if (host.contains("twitch.tv")) {
|
||||
seekUrl = true;
|
||||
timestamp = formatTimestampHMS(position, true);
|
||||
url = stripTimestampHMS(url, parameter, trailer, inQuery);
|
||||
}
|
||||
|
||||
if (seekUrl) {
|
||||
url = formatUrlWithSeek(url, timestamp, parameter, trailer, inQuery);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
// Returns timestamp in 1h2m34s or 01h02m34s (according to padWithZeroes)
|
||||
private static String formatTimestampHMS(long seconds, boolean padWithZeroes) {
|
||||
if (seconds == 0) {
|
||||
return "0s";
|
||||
}
|
||||
|
||||
int sec = (int) (seconds % SECONDS_IN_MINUTE);
|
||||
int min = (int) ((seconds / SECONDS_IN_MINUTE) % MINUTES_IN_HOUR);
|
||||
int hour = (int) (seconds / SECONDS_IN_HOUR);
|
||||
|
||||
String hours = hour > 0 ? hour + "h" : "";
|
||||
String mins = min > 0 || hour > 0 ? min + "m" : "";
|
||||
String secs = sec + "s";
|
||||
|
||||
String value;
|
||||
if (padWithZeroes) {
|
||||
String hoursPad = hour > 9 ? "" : "0";
|
||||
String minsPad = min > 9 ? "" : "0";
|
||||
String secsPad = sec > 9 ? "" : "0";
|
||||
value = hoursPad + hours + minsPad + mins + secsPad + secs;
|
||||
} else {
|
||||
value = hours + mins + secs;
|
||||
}
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
// Remove timestamp in 01h02m34s or 1h2m34s or 02m34s or 2m34s or 01s or 1s format.
|
||||
// Can also nandle rimestamps in 1234s format if called with 's' trailer
|
||||
private static URL stripTimestampHMS(URL url, String parameter, String trailer, boolean inQuery)
|
||||
throws MalformedURLException {
|
||||
String regex = parameter + "([\\d]+[hH])?([\\d]+[mM])?[\\d]+[sS]" + trailer + "&?";
|
||||
return stripTimestampCommon(url, inQuery, regex);
|
||||
}
|
||||
|
||||
|
||||
// Remove timestamp in 1234 format
|
||||
private static URL stripTimestampS(URL url, String parameter, String trailer, boolean inQuery)
|
||||
throws MalformedURLException {
|
||||
String regex = parameter + "[\\d]+" + trailer + "&?";
|
||||
return stripTimestampCommon(url, inQuery, regex);
|
||||
}
|
||||
|
||||
private static URL stripTimestampCommon(URL url, boolean inQuery, String regex)
|
||||
throws MalformedURLException {
|
||||
String value;
|
||||
if (inQuery) {
|
||||
value = url.getQuery();
|
||||
} else {
|
||||
value = url.getRef();
|
||||
}
|
||||
if (value == null) {
|
||||
return url;
|
||||
}
|
||||
String newValue = value.replaceAll(regex, "");
|
||||
String replaced = url.toString().replaceFirst(value, newValue);
|
||||
if (inQuery && replaced.endsWith("&")) {
|
||||
replaced = replaced.substring(0, replaced.length() - 1);
|
||||
}
|
||||
return new URL(replaced);
|
||||
}
|
||||
|
||||
private static URL formatUrlWithSeek(URL url, String position, String parameter, String trailer,
|
||||
boolean inQuery) throws MalformedURLException {
|
||||
String value;
|
||||
String separator;
|
||||
String newValue;
|
||||
if (inQuery) {
|
||||
value = url.getQuery();
|
||||
separator = "?";
|
||||
} else {
|
||||
value = url.getRef();
|
||||
separator = "#";
|
||||
}
|
||||
if (value == null) {
|
||||
newValue = String.format(Locale.getDefault(), "%s%s%s%s%s",
|
||||
url, separator, parameter, position, trailer);
|
||||
return new URL(newValue);
|
||||
}
|
||||
if (inQuery) {
|
||||
newValue = String.format(Locale.getDefault(), "%s&%s%s%s",
|
||||
value, parameter, position, trailer);
|
||||
} else {
|
||||
newValue = String.format(Locale.getDefault(), "%s%s%s",
|
||||
parameter, position, trailer);
|
||||
}
|
||||
return new URL(url.toString().replaceFirst(value, newValue));
|
||||
}
|
||||
}
|
80
src/org/kde/kdeconnect/Helpers/VideoUrlsHelper.kt
Normal file
80
src/org/kde/kdeconnect/Helpers/VideoUrlsHelper.kt
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 TPJ Schikhof <kde@schikhof.eu>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
|
||||
object VideoUrlsHelper {
|
||||
private const val SECONDS_IN_MINUTE = 60
|
||||
private const val MINUTES_IN_HOUR = 60
|
||||
private const val SECONDS_IN_HOUR = SECONDS_IN_MINUTE * MINUTES_IN_HOUR
|
||||
|
||||
@Throws(MalformedURLException::class)
|
||||
fun formatUriWithSeek(address: String, position: Long): URL {
|
||||
val positionSeconds = position / 1000 // milliseconds to seconds
|
||||
val url = URL(address)
|
||||
if (positionSeconds <= 0) {
|
||||
return url // nothing to do
|
||||
}
|
||||
val host = url.host.lowercase()
|
||||
|
||||
return when {
|
||||
listOf("youtube.com", "youtu.be", "pornhub.com").any { site -> site in host } -> {
|
||||
url.editParameter("t", Regex("\\d+")) { "$positionSeconds" }
|
||||
}
|
||||
host.contains("vimeo.com") -> {
|
||||
url.editParameter("t", Regex("\\d+s")) { "${positionSeconds}s" }
|
||||
}
|
||||
host.contains("dailymotion.com") -> {
|
||||
url.editParameter("start", Regex("\\d+")) { "$positionSeconds" }
|
||||
}
|
||||
host.contains("twitch.tv") -> {
|
||||
url.editParameter("t", Regex("(\\d+[hH])?(\\d+[mM])?\\d+[sS]")) { positionSeconds.formatTimestampHMS() }
|
||||
}
|
||||
else -> url
|
||||
}
|
||||
}
|
||||
|
||||
private fun URL.editParameter(parameter: CharSequence, valuePattern: Regex?, parameterValueModifier: (String) -> String): URL {
|
||||
// "https://www.youtube.com/watch?v=ovX5G0O5ZvA&t=13" -> ["https://www.youtube.com/watch", "v=ovX5G0O5ZvA&t=13"]
|
||||
val urlSplit = this.toString().split("?")
|
||||
if (urlSplit.size != 2) {
|
||||
return this
|
||||
}
|
||||
val (urlBase, urlQuery) = urlSplit
|
||||
val modifiedUrlQuery = urlQuery
|
||||
.split("&") // "v=ovX5G0O5ZvA&t=13" -> ["v=ovX5G0O5ZvA", "t=13"]
|
||||
.map { it.split("=", limit = 2) } // […, "t=13"] -> […, ["t", "13"]]
|
||||
.map { Pair(it.first(), it.lastOrNull() ?: return this) }
|
||||
.map { paramAndValue ->
|
||||
// Modify matching parameter and optionally matches the old value with the provided pattern
|
||||
if (paramAndValue.first == parameter && valuePattern?.matches(paramAndValue.second) != false) {
|
||||
Pair(paramAndValue.first, parameterValueModifier(paramAndValue.second)) // ["t", "13"] -> ["t", result]
|
||||
} else {
|
||||
paramAndValue
|
||||
}
|
||||
}
|
||||
.joinToString("&") { "${it.first}=${it.second}" } // [["v", "ovX5G0O5ZvA"], ["t", "14"]] -> "v=ovX5G0O5ZvA&t=14"
|
||||
return URL("${urlBase}?${modifiedUrlQuery}") // -> "https://www.youtube.com/watch?v=ovX5G0O5ZvA&t=14"
|
||||
}
|
||||
|
||||
/** Formats timestamp to e.g. 01h02m34s */
|
||||
private fun Long.formatTimestampHMS(): String {
|
||||
if (this == 0L) return "0s"
|
||||
|
||||
val seconds: Long = this % SECONDS_IN_MINUTE
|
||||
val minutes: Long = (this / SECONDS_IN_MINUTE) % MINUTES_IN_HOUR
|
||||
val hours: Long = this / SECONDS_IN_HOUR
|
||||
|
||||
fun pad(s: String) = s.padStart(3, '0')
|
||||
val hoursText = if (hours > 0) pad("${hours}h") else ""
|
||||
val minutesText = if (minutes > 0 || hours > 0) pad("${minutes}m") else ""
|
||||
val secondsText = pad("${seconds}s")
|
||||
|
||||
return "${hoursText}${minutesText}${secondsText}"
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user