Compare commits
8 Commits
work/1-26-
...
kdeconnect
Author | SHA1 | Date | |
---|---|---|---|
|
26aef16bbc | ||
|
abd4d02096 | ||
|
dbd184eb2e | ||
|
851fb58712 | ||
|
b7b9a3ad66 | ||
|
27bbbc2bf9 | ||
|
f70dc84ef2 | ||
|
6e9cbfb030 |
@@ -7,7 +7,7 @@
|
||||
# - Set up gitlab-runner, as described here: https://stackoverflow.com/a/52724374
|
||||
# - Run `gitlab-runner exec docker --docker-privileged assembleDebug`
|
||||
|
||||
image: eclipse-temurin:17-jdk-focal
|
||||
image: openjdk:11-jdk
|
||||
|
||||
variables:
|
||||
ANDROID_COMPILE_SDK: "31"
|
||||
|
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
BIN
.idea/icon.png
generated
Normal file
After Width: | Height: | Size: 4.2 KiB |
@@ -2,8 +2,15 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.kde.kdeconnect_tp"
|
||||
android:versionCode="12591"
|
||||
android:versionName="1.26.0 beta2">
|
||||
android:versionCode="12400"
|
||||
android:versionName="1.24.0">
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:xlargeScreens="true" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
@@ -52,8 +59,7 @@
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:theme="@style/KdeConnectTheme.NoActionBar"
|
||||
android:name="org.kde.kdeconnect.KdeConnect"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
android:name="org.kde.kdeconnect.MyApplication">
|
||||
|
||||
<receiver
|
||||
android:name="com.android.mms.transaction.PushReceiver"
|
||||
@@ -202,21 +208,21 @@
|
||||
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandWidgetConfigActivity"
|
||||
android:name="org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandWidgetDeviceSelector"
|
||||
android:excludeFromRecents="true"
|
||||
android:label="@string/pref_plugin_runcommand"
|
||||
android:launchMode="singleTask"
|
||||
android:noHistory="true"
|
||||
android:screenOrientation="user"
|
||||
android:theme="@style/Theme.Material3.DayNight.Dialog" />
|
||||
android:theme="@style/Theme.AppCompat.Light.Dialog" />
|
||||
|
||||
<service
|
||||
android:name="org.kde.kdeconnect.Plugins.RunCommandPlugin.CommandsRemoteViewsService"
|
||||
android:name="org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandWidgetDataProviderService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
|
||||
<receiver
|
||||
android:name="org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandWidgetProvider"
|
||||
android:name="org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandWidget"
|
||||
android:label="@string/pref_plugin_runcommand"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
@@ -268,10 +274,9 @@
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.Plugins.MousePadPlugin.ComposeSendActivity"
|
||||
android:label="@string/compose_send_title"
|
||||
android:label="Compose send"
|
||||
android:exported="false"
|
||||
android:parentActivityName="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
android:parentActivityName="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity" />
|
||||
@@ -359,7 +364,6 @@
|
||||
<action android:name="android.service.chooser.ChooserTargetService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<!--
|
||||
<service
|
||||
android:name="org.kde.kdeconnect.Plugins.MouseReceiverPlugin.MouseReceiverService"
|
||||
android:exported="true"
|
||||
@@ -371,7 +375,6 @@
|
||||
android:name="android.accessibilityservice"
|
||||
android:resource="@xml/mouse_receiver_service" />
|
||||
</service>
|
||||
-->
|
||||
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationFilterActivity"
|
||||
|
47
build.gradle
@@ -4,9 +4,9 @@ import com.android.build.gradle.api.ApplicationVariant
|
||||
import com.github.jk1.license.render.TextReportRenderer
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.8.21'
|
||||
ext.kotlin_version = '1.8.0'
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.0.2'
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
namespace 'org.kde.kdeconnect_tp'
|
||||
compileSdkVersion 33
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
@@ -29,13 +28,7 @@ android {
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
compose true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.4.7"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
@@ -88,18 +81,6 @@ android {
|
||||
abortOnError false
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
testOptions {
|
||||
unitTests.all {
|
||||
jvmArgs += [
|
||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
|
||||
"--add-opens", "java.base/java.security=ALL-UNNAMED",
|
||||
"--add-opens", "java.base/sun.security.rsa=ALL-UNNAMED",
|
||||
"--add-opens", "java.base/sun.security.x509=ALL-UNNAMED",
|
||||
"--add-opens", "java.base/java.util=ALL-UNNAMED",
|
||||
"--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,31 +136,23 @@ ext {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
|
||||
implementation 'androidx.compose.material3:material3:1.1.0'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview:1.4.3'
|
||||
implementation 'androidx.activity:activity-compose:1.7.2'
|
||||
implementation 'com.google.accompanist:accompanist-themeadapter-material3:0.31.0-alpha'
|
||||
implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.1'
|
||||
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview:1.4.3'
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling:1.4.3'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.2'
|
||||
|
||||
implementation 'androidx.media:media:1.6.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.core:core-ktx:1.10.1'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.0"
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.0'
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.6.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.6.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'com.jakewharton:disklrucache:2.0.2' //For caching album art bitmaps
|
||||
implementation 'com.jaredrummler:android-device-names:1.1.9' //To get a human-friendly device name
|
||||
|
||||
implementation 'org.apache.sshd:sshd-core:0.14.0'
|
||||
implementation 'org.apache.mina:mina-core:2.0.19' //For some reason, makes sshd-core:0.14.0 work without NIO, which isn't available until Android 8 (api 26)
|
||||
@@ -202,8 +175,6 @@ dependencies {
|
||||
implementation 'org.apache.commons:commons-collections4:4.4'
|
||||
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||
|
||||
implementation 'com.univocity:univocity-parsers:2.9.1'
|
||||
|
||||
// Kotlin
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||
|
@@ -1,2 +0,0 @@
|
||||
json_key_file("/please/pass/the/key/using/--json-key/instead")
|
||||
package_name("org.kde.kdeconnect_tp")
|
@@ -1,3 +0,0 @@
|
||||
1.25.0:
|
||||
* Rewrite some of the internals to improve performance.
|
||||
* Add search by name in the notification plugin settings.
|
@@ -1,14 +0,0 @@
|
||||
KDE Connect provides a set of features to integrate your workflow across devices:
|
||||
|
||||
- Shared clipboard: copy and paste between your devices.
|
||||
- Share files and URLs to your computer from any app.
|
||||
- Get notifications for incoming calls and SMS messages on your PC.
|
||||
- Virtual touchpad: Use your phone screen as your computer's touchpad.
|
||||
- Notifications sync: Read your Android notifications from the desktop.
|
||||
- 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
Before Width: | Height: | Size: 698 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 216 KiB |
Before Width: | Height: | Size: 158 KiB |
@@ -1 +0,0 @@
|
||||
KDE Connect integrates your smartphone and computer
|
@@ -1 +0,0 @@
|
||||
KDE Connect
|
@@ -1,4 +1,3 @@
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.enableJetifier=false
|
||||
android.useAndroidX=true
|
||||
org.gradle.jvmargs=-Xmx4096m
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
||||
|
@@ -18,7 +18,7 @@ msgstr ""
|
||||
"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"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
|
||||
#: dummy:1
|
||||
msgid "Integrate Android with the KDE Plasma Desktop."
|
||||
|
@@ -12,7 +12,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Accelerator-Marker: &\n"
|
||||
"X-Text-Markup: kde4\n"
|
||||
"X-Generator: Lokalize 19.04.1\n"
|
||||
|
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2019-06-30 11:38+0200\n"
|
||||
"PO-Revision-Date: 2023-06-02 05:46+0200\n"
|
||||
"PO-Revision-Date: 2022-09-29 06:55+0200\n"
|
||||
"Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: ka\n"
|
||||
@@ -16,7 +16,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 3.3.1\n"
|
||||
"X-Generator: Poedit 3.1.1\n"
|
||||
|
||||
#: dummy:1
|
||||
msgid "Integrate Android with the KDE Plasma Desktop."
|
||||
@@ -50,31 +50,3 @@ msgid ""
|
||||
"This app is part of an open source project, visit the website to grab the "
|
||||
"sources.\n"
|
||||
msgstr ""
|
||||
"KDE Connect გთავაზობთ რამდენიმე მახასიათებელს თქვენი ტელეფონისა და "
|
||||
"კომპიუტერის ინტეგრირებისთვის:\n"
|
||||
"\n"
|
||||
"- გაუზიარეთ ფაილები და URL KDE ნებისმიერი აპლიკაციიდან, მავთულის გარეშე.\n"
|
||||
"- სენსორული პანელის ემულაცია: გამოიყენეთ თქვენი ტელეფონის ეკრანი, როგორც "
|
||||
"თქვენი კომპიუტერის სენსორული პანელი *.\n"
|
||||
"- შეტყობინებების სინქრონიზაცია (4.3+): წაიკითხეთ თქვენი Android "
|
||||
"შეტყობინებები სამუშაო მაგიდიდან.\n"
|
||||
"- გაზიარებული ბუფერი: დააკოპირეთ და ჩასვით თქვენს ტელეფონსა და კომპიუტერს "
|
||||
"შორის.\n"
|
||||
"- მულტიმედიური დისტანციური მართვა: გამოიყენეთ თქვენი ტელეფონი როგორც "
|
||||
"დისტანციური მართვის საშუალებით Linux მედია ფლეერებისთვის.\n"
|
||||
"- WiFi კავშირი: არ არის საჭირო usb მავთული ან Bluetooth.\n"
|
||||
"- RSA დაშიფვრა: თქვენი ინფორმაცია უსაფრთხოა.\n"
|
||||
"\n"
|
||||
"გთხოვთ გაითვალისწინოთ, რომ თქვენ უნდა დააინსტალიროთ KDE Connect თქვენს "
|
||||
"კომპიუტერში, რომ ამ აპმა იმუშაოს და შეინარჩუნოს დესკტოპის ვერსია განახლებული "
|
||||
"Android ვერსიით უახლესი ფუნქციების მუშაობისთვის.\n"
|
||||
"\n"
|
||||
"*შენიშვნა Ubuntu მომხმარებლებისთვის: Ubuntu ხალხი არ განაახლებს მათ რეპოსებს "
|
||||
"ისე სწრაფად, როგორც ეს აპლიკაცია განახლდება. ზოგიერთი ფუნქცია არ იმუშავებს, "
|
||||
"თუ KDE Connect ვერსია თქვენში სამუშაო მაგიდა არ ემთხვევა თქვენს ტელეფონში. "
|
||||
"იმისათვის, რომ დარწმუნდეთ, რომ ყოველთვის გაქვთ უახლესი ვერსია თქვენს სამუშაო "
|
||||
"მაგიდაზე, გამოიყენეთ ეს PPA საცავი: https://code.launchpad.net/~vikoadi/"
|
||||
"+archive/ubuntu/ppa/ \n"
|
||||
"\n"
|
||||
"ეს აპლიკაცია არის ღია კოდის პროექტის ნაწილი, ეწვიეთ ვებსაიტს წყაროების "
|
||||
"ასაღებად.\n"
|
||||
|
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: kdeorg\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2019-06-30 11:38+0200\n"
|
||||
"PO-Revision-Date: 2023-05-22 14:00\n"
|
||||
"PO-Revision-Date: 2023-04-10 14:10\n"
|
||||
"Last-Translator: Albert Vaca Cintora <albertvaka@gmail.com>\n"
|
||||
"Language-Team: Chinese Simplified\n"
|
||||
"Language: zh_CN\n"
|
||||
|
@@ -1,5 +0,0 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
</vector>
|
@@ -1,9 +1,9 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/navigation_drawer"
|
||||
@@ -16,8 +16,7 @@
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||
android:fitsSystemWindows="true">
|
||||
tools:context="org.kde.kdeconnect.UserInterface.MainActivity">
|
||||
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout" />
|
||||
|
||||
|
@@ -5,8 +5,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.About.AboutKDEActivity"
|
||||
android:fitsSystemWindows="true">
|
||||
tools:context="org.kde.kdeconnect.UserInterface.About.AboutKDEActivity">
|
||||
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout" />
|
||||
|
||||
|
50
res/layout/activity_compose_send.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/compose"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:ems="10"
|
||||
android:hint="@string/click_here_to_type"
|
||||
android:imeActionLabel="@string/send_compose"
|
||||
android:imeOptions="actionSend|actionDone"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textLongMessage|textMultiLine"
|
||||
android:isScrollContainer="true"
|
||||
android:saveEnabled="true"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout2"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
tools:ignore="SpeakableTextPresentCheck,TextContrastCheck" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -6,8 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.CustomDevicesActivity"
|
||||
android:fitsSystemWindows="true">
|
||||
tools:context="org.kde.kdeconnect.UserInterface.CustomDevicesActivity">
|
||||
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout" />
|
||||
|
||||
|
@@ -1,33 +1,51 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.DeviceFragment">
|
||||
|
||||
<!-- Shown when device is reachable but not yet paired -->
|
||||
<include
|
||||
android:id="@+id/pair_request"
|
||||
layout="@layout/view_pair_request"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<!-- Shown when the device is paired but not reachable -->
|
||||
<include
|
||||
android:id="@+id/pair_error"
|
||||
layout="@layout/view_pair_error"
|
||||
tools:visibility="gone"/>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/device_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- Shown when the device is paired and reachable -->
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/device_view_compose"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.DeviceFragment">
|
||||
|
||||
<!-- Layout shown when device is reachable but not yet paired -->
|
||||
<include
|
||||
android:id="@+id/pair_request"
|
||||
layout="@layout/view_pair_request"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<!-- Layout shown when we can't pair with device or device is not reachable -->
|
||||
<include
|
||||
android:id="@+id/pair_error"
|
||||
layout="@layout/view_pair_error"
|
||||
tools:visibility="gone"/>
|
||||
|
||||
<!-- Layouts shown when device is paired and reachable -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/plugins_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
tools:listitem="@layout/list_plugin_entry"
|
||||
tools:layout_height="300dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/permissions_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nestedScrollingEnabled="false"
|
||||
tools:context=".DeviceActivity"
|
||||
tools:listitem="@layout/list_item_plugin_header"
|
||||
tools:layout_height="300dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</FrameLayout>
|
@@ -4,7 +4,6 @@
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.About.LicensesActivity">
|
||||
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout" />
|
||||
|
@@ -1,33 +1,37 @@
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_height="match_parent"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
/>
|
||||
android:fitsSystemWindows="true"> <!-- fitSystemWindows to make the drawer slide below the Lollipop transparent status bar -->
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.MainActivity">
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/navigation_drawer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
app:headerLayout="@layout/nav_header"/>
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout"/>
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/navigation_drawer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
app:headerLayout="@layout/nav_header"/>
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
</FrameLayout>
|
@@ -9,7 +9,9 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="8dp"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
@@ -19,7 +21,10 @@
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/mpris_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/toolbar_color"
|
||||
app:tabIndicatorColor="?android:textColorPrimary"
|
||||
app:tabSelectedTextColor="?android:textColorPrimary" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
@@ -6,8 +6,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
|
||||
android:fitsSystemWindows="true">
|
||||
tools:context="org.kde.kdeconnect.UserInterface.PluginSettingsActivity">
|
||||
|
||||
<include layout="@layout/toolbar" android:id="@+id/toolbar_layout" />
|
||||
|
||||
|
@@ -14,7 +14,7 @@
|
||||
android:divider="@null"
|
||||
android:dividerHeight="12dp"
|
||||
android:orientation="vertical"
|
||||
tools:listitem="@layout/list_card_entry"
|
||||
tools:listitem="@layout/list_plugin_entry"
|
||||
tools:context=".MainActivity" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
@@ -15,7 +15,7 @@
|
||||
android:id="@+id/textInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:hintEnabled="true">
|
||||
app:hintEnabled="false">
|
||||
|
||||
<!-- inputType="text" is needed, without it lines and maxLines is ignored https://issuetracker.google.com/issues/37118772 -->
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
|
@@ -3,7 +3,6 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical"
|
||||
@@ -21,8 +20,6 @@
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:text=""
|
||||
android:textColor="@color/text_color"
|
||||
tools:text="Item entry"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
|
@@ -5,11 +5,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:fitsSystemWindows="true">
|
||||
android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:elevation="8dp"
|
||||
app:title="@string/kde_connect"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
@@ -1,41 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
<LinearLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/error_message_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/error_message_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:paddingEnd="8dip"
|
||||
android:paddingStart="0dip"
|
||||
android:src="@drawable/ic_error_outline_48dp"
|
||||
app:tint="?attr/colorHighContrast"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/not_reachable_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/error_message_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:importantForAccessibility="no"
|
||||
android:paddingEnd="8dip"
|
||||
android:paddingStart="0dip"
|
||||
android:src="@drawable/ic_error_outline_48dp"
|
||||
app:tint="?attr/colorHighContrast"
|
||||
tools:ignore="UnusedAttribute" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/not_reachable_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/unreachable_description"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/unreachable_description"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
@@ -6,7 +6,7 @@
|
||||
android:id="@+id/pairing_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/activity_vertical_margin"
|
||||
android:visibility="gone"
|
||||
|
@@ -15,14 +15,4 @@
|
||||
android:divider="@null"
|
||||
android:dividerHeight="0dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_devices"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="10dip"
|
||||
android:text="@string/device_list_empty"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
@@ -1,42 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/activity_background"
|
||||
android:background="@color/on_secondary"
|
||||
android:theme="@style/KdeConnectTheme"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
android:layout_width="match_parent">
|
||||
<LinearLayout
|
||||
android:id="@+id/runcommandWidgetTitleHeader"
|
||||
android:background="@color/on_secondary"
|
||||
android:background="@color/primary"
|
||||
android:gravity="center_vertical|start"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:padding="8dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:paddingTop="6dip"
|
||||
android:paddingBottom="6dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:tint="@color/text_color"
|
||||
android:src="@drawable/ic_kde_24dp"
|
||||
android:contentDescription="@string/device_icon_description"
|
||||
tools:ignore="UseAppTint"/> <!-- can't use app:tint in RemoteView -->
|
||||
android:src="@drawable/ic_kde_48dp"
|
||||
android:contentDescription="@string/device_icon_description"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/runcommandWidgetTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="marquee"
|
||||
android:textColor="@color/text_color"
|
||||
android:text="@string/kde_connect"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<ListView
|
||||
android:id="@+id/run_commands_list"
|
||||
android:layout_width="match_parent"
|
||||
@@ -54,6 +48,5 @@
|
||||
android:paddingStart="8dip"
|
||||
android:text="@string/runcommand_notreachable"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:drawableStart="@drawable/ic_error_outline_48dp"
|
||||
tools:ignore="UseCompatTextViewDrawableXml" /> <!-- can't use app:drawableStart in RemoteView -->
|
||||
android:drawableStart="@drawable/ic_error_outline_48dp"/>
|
||||
</LinearLayout>
|
15
res/menu/menu_compose_send.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:kdeconnect="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_send_compose"
|
||||
android:icon="@android:drawable/ic_menu_send"
|
||||
android:title="@string/send_compose"
|
||||
kdeconnect:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/menu_clear_compose"
|
||||
android:title="@string/clear_compose"
|
||||
kdeconnect:showAsAction="always" />
|
||||
|
||||
</menu>
|
@@ -6,13 +6,13 @@
|
||||
android:id="@+id/menu_rise_up"
|
||||
android:icon="@drawable/ic_arrow_upward_black_24dp"
|
||||
android:title="@string/rise_up"
|
||||
kdeconnect:iconTint="?colorOnSurfaceVariant"
|
||||
android:iconTint="@color/text_color"
|
||||
kdeconnect:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_rise_down"
|
||||
android:icon="@drawable/ic_arrow_downward_black_24dp"
|
||||
android:title="@string/rise_down"
|
||||
kdeconnect:iconTint="?colorOnSurfaceVariant"
|
||||
android:iconTint="@color/text_color"
|
||||
kdeconnect:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
|
@@ -51,17 +51,13 @@
|
||||
<string name="remotekeyboard_connected">Uzaq klaviatura bağlantısını aktiv edin</string>
|
||||
<string name="remotekeyboard_multiple_connections">Birdən çox uzaq klaviatura bağlantısı var, tənzimləmək üçün cihazı seçin</string>
|
||||
<string name="open_mousepad">Məsafədən giriş</string>
|
||||
<string name="mousepad_info">Siçan kursorunu hərəkət etdirmək üçün barmağı ekranda sürüşdürün. Klik üçün ekrana vurun, sağ və orta siçan düymələri üçün iki/üç barmaqla toxunuş edin. Sürüşdürmək üçün iki barmaqdan istifadə edin. Hiroskop siçan funksionallığı plaqin ayarlarında aktiv edilməlidir</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Qoşulmuş cihaz üçün klaviatura ilə daxiletmə dəstəklənmir</string>
|
||||
<string name="mousepad_single_tap_settings_title">Bir barmaq toxunuşu əməlini təyin edin</string>
|
||||
<string name="mousepad_double_tap_settings_title">İki barmaq toxunuşu əməlini təyin edin</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Üç barmaq toxunuşu əməlini təyin edin</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Toxunma panelinin həsassləğını təyin edin</string>
|
||||
<string name="mousepad_mouse_buttons_title">Siçan düymələrini göstərmək</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Kursorun sürətini təyin edin</string>
|
||||
<string name="mousepad_scroll_direction_title">Sürüşdürmənin əks istiqaməti</string>
|
||||
<string name="gyro_mouse_enabled_title">Qiroskoplu siçanı qoşun</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Qiroskopun həssaslığı</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Sol toxunuş</item>
|
||||
<item>Sağ toxunuş</item>
|
||||
@@ -85,7 +81,7 @@
|
||||
</string-array>
|
||||
<string name="sendkeystrokes_send_to">Düymələrə vuruşları buraya göndərin:</string>
|
||||
<string name="sendkeystrokes_textbox_hint">Düyməyə vurulmanı ana komputerə göndərin</string>
|
||||
<string name="sendkeystrokes_disabled_toast">Düymə vuruşun göndərilməsi söndürülüb - onu ayarlardan aktiv edin</string>
|
||||
<string name="sendkeystrokes_disabled_toast">Düyməə vuruşun göndərilməsi söndürülüb - onu ayarlardan aktiv edin</string>
|
||||
<string name="sendkeystrokes_wrong_data">Səhv mime növü - \'text/x-keystrokes\' üçün lazımdır</string>
|
||||
<string name="sendkeystrokes_sent_text">%2s cihazına %1s göndərildi</string>
|
||||
<string name="sendkeystrokes_pref_category_summary">Bu modul, düyməyə vurmaqla, qoşulmuş ana komputerə göndərərərk başqa tətbiqlərin mətn hissəsininlərini mübadilə etməyə imkan verir</string>
|
||||
@@ -220,11 +216,8 @@
|
||||
<string name="sftp_action_mode_menu_delete">Silmək</string>
|
||||
<string name="sftp_no_storage_locations_configured">Saxlama yeri tənzimlənməyib</string>
|
||||
<string name="sftp_saf_permission_explanation">Fayllara uzaqdan daxil olmaq üçün saxlama yerlərini konfiqurasiya etməlisiniz</string>
|
||||
<string name="sftp_manage_storage_permission_explanation">Bu cihazdakı fayllara giriş əldə etmək üçün KDE Connect-ə yaddaşı idarə etməyə icazə vermək lazımdır.</string>
|
||||
<string name="no_players_connected">Pleyer tapılmadı</string>
|
||||
<string name="send_files">Faylları göndərmək</string>
|
||||
<string name="block_notification_contents">Bildirilərin tərkiblərini kilidləmək</string>
|
||||
<string name="block_notification_images">Bildiriş şəlkillərini kilidləmək</string>
|
||||
<string name="pairing_title">KDE Connect Cihazları</string>
|
||||
<string name="pairing_description">Eyni şəbəkədəki KDE Connect işləyən digər cihazlar burada görünməlidir</string>
|
||||
<string name="device_rename_title">Cihazın adını dəyişmək</string>
|
||||
@@ -247,10 +240,8 @@
|
||||
<string name="close">Bağlamaq</string>
|
||||
<string name="plugins_need_permission">Bəzi qoşmaların işləməsi üçün icazələr lazımdır (daha çox məlumat üçün toxunun):</string>
|
||||
<string name="permission_explanation">Bu qoşmanın işləməsi üçün icazələr lazımdır</string>
|
||||
<string name="all_permissions_granted">Bütün icazələr verildi 🎉</string>
|
||||
<string name="optional_permission_explanation">Bütün funksiyaların işləməsi üçün əlavə icazələr verməlisiniz</string>
|
||||
<string name="plugins_need_optional_permission">Bəzi qoşmalarda icazə çatışmamazlığı səbəbindən bir sıra imkanlar söndürülmüşdür (daha çox məlumat üçün toxunun)</string>
|
||||
<string name="share_optional_permission_explanation">Faylları qəbul etmək üçün yaddaşa girişə icazə verilməlidirü</string>
|
||||
<string name="telepathy_permission_explanation">İş Masanızdan telefonunuzdakı SMS\'ləri oxumaq və SMS göndərmək üçün SMS\'ə girişə icazə verməlisiniz</string>
|
||||
<string name="telephony_permission_explanation">İş Masanızda telefon zənglərini görmək üçün Zəng Tarixçəsinə və Zəng yığımı vəziyyətinə icazə verməlisiniz</string>
|
||||
<string name="telephony_optional_permission_explanation">Telefon nömrəsi əvəzinə əlaqənin adını görmək üçün Əlaqə Kitabçasına girişə icazə verməlisiniz</string>
|
||||
@@ -376,7 +367,6 @@
|
||||
<string name="click_here_to_type">Yazmaq üçün toxunun</string>
|
||||
<string name="clear_compose">Təmizləmək</string>
|
||||
<string name="send_compose">Göndərin</string>
|
||||
<string name="compose_send_title">Yazın göndərin</string>
|
||||
<string name="open_compose_send">Mətn yazın</string>
|
||||
<string name="about_kde_about"><h1>Haqqında</h1> <p>KDE, özlərini <a href=https://www.gnu.org/philosophy/free-sw.html>Azad Proqram Təminatı</a> tərtibatına həsr etmiş, proqram təminatı üzrə mühəndislərdən, rəssamlardan, yazıçılardan, tərcüməçilərdən və yaradıcılardan ibarət dünya miqyasında bir cəmiyyətdir. KDE, Plasma İş masası mühiti, yüzlərlə tətbiqlər və onları dəstəkləyən proqram təminatı kitabxanaları nəşr edir. </p> <p>KDE kooperativ bir müəssisədir: heç bir müəssisə onun işinə və məhsullarına nəzarət etmir. Bunun əvəzinə, dünyanın ən yaxşı Proqram Təminatını yaratmaq məsədimizə çatmaq üçün birlikdə çalışırıq. <a href=https://community.kde.org/Get_Involved>KDE\'yə qoşulmaq və işimizə töhvə vermək istəyən, </a> siz də daxil olmaqla</p> hər kəsi salamlayırıq. <a href=https://www.kde.org/>https://www.kde.org/</a> saytına daxil olun ki, KDE cəmiyyəti və bizim proqram təminatı və məhsullarımız haqqında daha çox məlumat əldə edə biləsiniz.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Səhvləri və Arzularınızı bildirin</h1> <p>Proqram təminatı hər zaman təkmilləşdirilə bilər və KDE komandası buna hazırdır. Lakin siz - istifadəçi - , nə isə gözlənildiyi kimi düzgün işləmədikdə və ya bundan daha yaxşısını yaratmaq barədə təklifiniz olduqda bizə bildirməlisiniz.</p> <p>KDE\'nin xətaları izləmə sistemi var. <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> ziyarət edin və ya xətalar haqında hesabat vermək üçün ekrandakı \"Xətaları bildirin\" düyməsindən istifadə edin.</p> Əgər təkmilləşdirmə üçün bir təklifiniz varsa, bu istəyinizi bildirmək üçün xətaları izləmə sistemində qeyd edə bilərsiniz. \"İstəklər siyahısı\" ciddilik səviyyəsindən istifadə etdiyinizə əmin olun.</string>
|
||||
|
@@ -51,17 +51,13 @@
|
||||
<string name="remotekeyboard_connected">Отдалечената връзка с клавиатурата е активна</string>
|
||||
<string name="remotekeyboard_multiple_connections">Има повече от една отдалечена връзка за клавиатура, изберете устройството за конфигуриране</string>
|
||||
<string name="open_mousepad">Отдалечен вход</string>
|
||||
<string name="mousepad_info">Преместете пръст на екрана, за да преместите курсора на мишката. Докоснете за щракване и използвайте два/три пръста за десни и средни бутони. Използвайте 2 пръста за превъртане.Използвайте дълго натискане за влачене. Функцията на жироскопската мишка може да бъде активирана в настройките на плъгина</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>
|
||||
<string name="mousepad_triple_tap_settings_title">Задаване на действие с докосване с три пръста</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Настройка на чувствителността на тъчпада</string>
|
||||
<string name="mousepad_mouse_buttons_title">Показване на бутони на мишката</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Задаване на ускорение на показалеца</string>
|
||||
<string name="mousepad_scroll_direction_title">Обръщане на посоката на превъртане</string>
|
||||
<string name="gyro_mouse_enabled_title">Активиране на жироскопската мишка</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Чувствителност на жироскопа</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Щракване с ляв бутон</item>
|
||||
<item>Щракване с десен бутон</item>
|
||||
@@ -376,7 +372,6 @@
|
||||
<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="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>
|
||||
|
@@ -60,8 +60,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">Mostra els botons del ratolí</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Estableix l\'acceleració de l\'apuntador</string>
|
||||
<string name="mousepad_scroll_direction_title">Inverteix la direcció del desplaçament</string>
|
||||
<string name="gyro_mouse_enabled_title">Activa el ratolí giroscòpic</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Sensibilitat del giroscopi</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Clic esquerre</item>
|
||||
<item>Clic dret</item>
|
||||
@@ -108,7 +106,6 @@
|
||||
<string name="device_menu_plugins">Arranjament dels connectors</string>
|
||||
<string name="device_menu_unpair">Desaparella</string>
|
||||
<string name="pair_new_device">Aparella amb un dispositiu nou</string>
|
||||
<string name="cancel_pairing">Cancel·la l\'aparellament</string>
|
||||
<string name="unknown_device">Dispositiu desconegut</string>
|
||||
<string name="error_not_reachable">No es pot accedir al dispositiu</string>
|
||||
<string name="error_already_paired">El dispositiu ja està aparellat</string>
|
||||
@@ -377,7 +374,6 @@
|
||||
<string name="click_here_to_type">Toqueu aquí per a teclejar</string>
|
||||
<string name="clear_compose">Neteja</string>
|
||||
<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="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>
|
||||
|
@@ -51,13 +51,11 @@
|
||||
<string name="remotekeyboard_connected">Vzdálené připojení klávesnice je aktivní</string>
|
||||
<string name="remotekeyboard_multiple_connections">Je k dispozici více než jedno připojení klávesnice. Vyberte zařízení pro jeho nastavení.</string>
|
||||
<string name="open_mousepad">Vzdálený vstup</string>
|
||||
<string name="mousepad_info">Pohybujte prstem po obrazovce pro pohybování kurzorem myši. Ťukněte pro kliknutí a použijte dva/tři prsty jako pravé a prostřední tlačítko. Použijte 2 prsty pro posunování. Pro přetažení dlouze podržte. Funkčnost gyro myš lze povolit v předvolbách modulu.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Vstup pomocí klávesnice není spárovaným zařízením podporován</string>
|
||||
<string name="mousepad_single_tap_settings_title">Nastavit činnost pro ťuknutí prstem</string>
|
||||
<string name="mousepad_double_tap_settings_title">Nastavit činnost pro ťuknutí dvěma prsty</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Nastavit činnost pro ťuknutí třemi prsty</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Nastavit citlivost touchpadu</string>
|
||||
<string name="mousepad_mouse_buttons_title">Zobrazit tlačítka myši</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Nastavit akceleraci ukazatele</string>
|
||||
<string name="mousepad_scroll_direction_title">Obrácený směr posunu</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<string name="kde_connect">KDE Connect</string>
|
||||
<string name="manifest_label_share">An Gerät senden</string>
|
||||
<string name="foreground_notification_no_devices">Keine bestehenden Verbindungen</string>
|
||||
<string name="foreground_notification_devices">Verbunden mit %s</string>
|
||||
<string name="foreground_notification_send_clipboard">Zwischenablage senden</string>
|
||||
@@ -51,23 +50,11 @@
|
||||
<string name="remotekeyboard_connected">Verbindung zu entfernter Tastatur ist aktiv</string>
|
||||
<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_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>
|
||||
<string name="mousepad_triple_tap_settings_title">Aktionsausführung bei Berührung mit drei Fingern einstellen</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Empfindlichkeit des Touchpads einstellen</string>
|
||||
<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="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">
|
||||
<item>Linksklick</item>
|
||||
<item>Rechtsklick</item>
|
||||
<item>Mittelklick</item>
|
||||
<item>Nichts</item>
|
||||
</string-array>
|
||||
<string-array name="mousepad_sensitivity_entries">
|
||||
<item>Langsamste</item>
|
||||
<item>Langsam</item>
|
||||
@@ -87,19 +74,11 @@
|
||||
<string name="sendkeystrokes_textbox_hint">Tastendruck an Rechner senden</string>
|
||||
<string name="sendkeystrokes_disabled_toast">Das Senden von Tastatureingaben ist deaktiviert - aktivieren Sie es in den Einstellungen</string>
|
||||
<string name="sendkeystrokes_wrong_data">Ungültiger MIME-Typ - er muss „text/x-keystrokes“ sein</string>
|
||||
<string name="sendkeystrokes_sent_text">%1$s an Gerät %2$s senden</string>
|
||||
<string name="sendkeystrokes_pref_category_summary">Mit diesem Modul können andere Anwendungen Textsegmente als Tastendrücke teilen, die an den verbundenen Rechner gesendet werden</string>
|
||||
<string name="sendkeystrokes_pref_category_title">Tastendrücke senden</string>
|
||||
<string name="sendkeystrokes_pref_enabled">Senden von Tastendrücken aktivieren</string>
|
||||
<string name="sendkeystrokes_pref_enabled_summary">Auf Daten mit dem MIME-Typ „text/x-keystrokes“ warten</string>
|
||||
<string name="pref_plugin_mousepad_send_keystrokes">Als Tastendruck senden</string>
|
||||
<string name="mouse_receiver_plugin_description">Empfänger für entfernte Mauseingaben</string>
|
||||
<string name="mouse_receiver_plugin_name">Maus-Empfänger</string>
|
||||
<string name="mouse_receiver_no_permissions">Sie müssen den Zugangshilfendienst aktivieren</string>
|
||||
<string name="view_status_title">Status</string>
|
||||
<string name="battery_status_format">Akku: %d%%</string>
|
||||
<string name="battery_status_low_format">Akku: %d%% Niedriger Ladestand</string>
|
||||
<string name="battery_status_charging_format">Akku: %d%% Wird geladen</string>
|
||||
<string name="category_connected_devices">Verbundene Geräte</string>
|
||||
<string name="category_not_paired_devices">Verfügbare Geräte</string>
|
||||
<string name="category_remembered_devices">Gemerkte Geräte</string>
|
||||
@@ -168,8 +147,6 @@
|
||||
<string name="mpris_rew">Schneller Rücklauf</string>
|
||||
<string name="mpris_ff">Vorlauf</string>
|
||||
<string name="mpris_next">Weiter</string>
|
||||
<string name="mpris_loop">Wiederholen</string>
|
||||
<string name="mpris_shuffle">Mischen</string>
|
||||
<string name="mpris_volume">Lautstärke</string>
|
||||
<string name="mpris_time_settings_title">Knöpfe Vorwärts/Rückwärts</string>
|
||||
<string name="mpris_time_settings_summary">Sprungweite für Vorlauf/Rücklauf anpassen</string>
|
||||
@@ -203,7 +180,6 @@
|
||||
<string name="share_received_file">%s freigeben</string>
|
||||
<string name="title_activity_notification_filter">Benachrichtigungs-Filter</string>
|
||||
<string name="filter_apps_info">Benachrichtigungen werden zwischen den ausgewählten Anwendungen abgeglichen</string>
|
||||
<string name="show_notification_if_screen_off">Nur bei ausgeschaltetem Bildschirm Benachrichtigungen senden</string>
|
||||
<string name="add_device_dialog_title">Gerät hinzufügen</string>
|
||||
<string name="add_device_hint">Rechnername oder IP-Adresse</string>
|
||||
<string name="sftp_preference_configured_storage_locations">Vorhandene Speicherort</string>
|
||||
@@ -220,8 +196,6 @@
|
||||
<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="no_players_connected">Keine Medienspieler gefunden</string>
|
||||
<string name="send_files">Dateien senden</string>
|
||||
<string name="block_notification_contents">Benachrichtigungsinhalte blockieren</string>
|
||||
<string name="block_notification_images">Bilder in Benachrichtigungen blockieren</string>
|
||||
<string name="pairing_title">KDE-Connect-Geräte</string>
|
||||
<string name="pairing_description">Andere Geräte im selben Netzwerk, auf denen KDE Connect läuft, sollten hier angezeigt werden</string>
|
||||
<string name="device_rename_title">Geräte umbenennen</string>
|
||||
@@ -244,7 +218,6 @@
|
||||
<string name="close">Schließen</string>
|
||||
<string name="plugins_need_permission">Einige Module benötigen zusätzliche Berechtigungen, tippen Sie für weitere Details:</string>
|
||||
<string name="permission_explanation">Dieses Modul benötigt zusätzliche Berechtigungen</string>
|
||||
<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="telepathy_permission_explanation">Um SMS vom Rechner aus zu lesen und zu versenden, muss der Zugriff auf die SMS-Funktion gewährt werden</string>
|
||||
@@ -267,7 +240,6 @@
|
||||
<string name="pref_plugin_mprisreceiver">Steuerung der Medienwiedergabe</string>
|
||||
<string name="pref_plugin_mprisreceiver_desc">Ein verbundenes Gerät zum Steuern der lokalen Medienwiedergabe verwenden</string>
|
||||
<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">Dateiübertragung</string>
|
||||
<string name="notification_channel_high_priority">Hohe Priorität</string>
|
||||
@@ -314,7 +286,6 @@
|
||||
<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>
|
||||
<string name="bigscreen_home">Startseite</string>
|
||||
<string name="bigscreen_up">Oben</string>
|
||||
<string name="bigscreen_left">Links</string>
|
||||
<string name="bigscreen_select">Auswählen</string>
|
||||
@@ -352,25 +323,4 @@
|
||||
<string name="donate">Spenden</string>
|
||||
<string name="source_code">Quelltext</string>
|
||||
<string name="licenses">Lizenzen</string>
|
||||
<string name="website">Webseite</string>
|
||||
<string name="about">Über</string>
|
||||
<string name="authors">Autoren</string>
|
||||
<string name="thanks_to">Dank an</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>
|
||||
<string name="about_kde">Über KDE</string>
|
||||
<string name="kde_be_free">KDE - Freiheit genießen.</string>
|
||||
<string name="kde">KDE</string>
|
||||
<string name="konqi">Konqi</string>
|
||||
<string name="clear_compose">Leeren</string>
|
||||
<string name="send_compose">Senden</string>
|
||||
<string name="open_compose_send">Text schreiben</string>
|
||||
<string name="maintainer_and_developer">Betreuer und Entwickler</string>
|
||||
<string name="developer">Entwickler</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="send_clipboard">Zwischenablage senden</string>
|
||||
<string name="tap_to_execute">Tippen um auszuführen</string>
|
||||
</resources>
|
||||
|
@@ -51,17 +51,13 @@
|
||||
<string name="remotekeyboard_connected">Remote keyboard connection is active</string>
|
||||
<string name="remotekeyboard_multiple_connections">There is more than one remote keyboard connection, select the device to configure</string>
|
||||
<string name="open_mousepad">Remote input</string>
|
||||
<string name="mousepad_info">Move a finger on the screen to move the mouse cursor. Tap for a click, and use two/three fingers for right and middle buttons. Use 2 fingers to scroll. Use a long press to drag and drop. Gyro mouse functionality can be enabled from plugin preferences</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Keyboard input not supported by the paired device</string>
|
||||
<string name="mousepad_single_tap_settings_title">Set one finger tap action</string>
|
||||
<string name="mousepad_double_tap_settings_title">Set two finger tap action</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Set three finger tap action</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Set touchpad sensitivity</string>
|
||||
<string name="mousepad_mouse_buttons_title">Show mouse buttons</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Set pointer acceleration</string>
|
||||
<string name="mousepad_scroll_direction_title">Reverse Scrolling Direction</string>
|
||||
<string name="gyro_mouse_enabled_title">Enable gyroscope mouse</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Gyroscope sensitivity</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Left click</item>
|
||||
<item>Right click</item>
|
||||
@@ -376,7 +372,6 @@
|
||||
<string name="click_here_to_type">Tap here to type</string>
|
||||
<string name="clear_compose">Clear</string>
|
||||
<string name="send_compose">Send</string>
|
||||
<string name="compose_send_title">Compose send</string>
|
||||
<string name="open_compose_send">Compose text</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>
|
||||
|
@@ -51,17 +51,13 @@
|
||||
<string name="remotekeyboard_connected">La conexión remota de teclado está activa</string>
|
||||
<string name="remotekeyboard_multiple_connections">Hay más de una conexión remota de teclado, seleccione el dispositivo a configurar</string>
|
||||
<string name="open_mousepad">Entrada remota</string>
|
||||
<string name="mousepad_info">Mueva un dedo sobre la pantalla para mover el cursor del ratón. Pulse para ejecutar un clic y use dos/tres dedos para emular los botones derecho y central. Use 2 dedos para desplazar las pantalla. Use una pulsación larga para arrastrar y soltar. La funcionalidad de la rueda del ratón puede ser activada desde las preferencias del complemento.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Entrada de teclado no soportada por el dispositivo vinculado.</string>
|
||||
<string name="mousepad_single_tap_settings_title">Establecer la acción al pulsar con un dedo</string>
|
||||
<string name="mousepad_double_tap_settings_title">Establecer la acción al pulsar con dos dedos</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Establecer la acción al pulsar con tres dedos</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Establecer sensibilidad del panel táctil</string>
|
||||
<string name="mousepad_mouse_buttons_title">Mostrar botones del ratón</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Establecer la aceleración del puntero</string>
|
||||
<string name="mousepad_scroll_direction_title">Invertir dirección de desplazamiento</string>
|
||||
<string name="gyro_mouse_enabled_title">Activar ratón giroscópico</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Sensibilidad del giroscopio</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Clic izquierdo</item>
|
||||
<item>Clic derecho</item>
|
||||
@@ -376,7 +372,6 @@
|
||||
<string name="click_here_to_type">Pulse para escribir</string>
|
||||
<string name="clear_compose">Borrar</string>
|
||||
<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="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>
|
||||
|
@@ -180,7 +180,7 @@
|
||||
</string-array>
|
||||
<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="share_to">Partekatu honi...</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>
|
||||
|
@@ -51,13 +51,11 @@
|
||||
<string name="remotekeyboard_connected">Etänäppäimistöyhteys on käytössä</string>
|
||||
<string name="remotekeyboard_multiple_connections">Etänäppäimistöyhteyksiä on useampia: valitse asetettava laite</string>
|
||||
<string name="open_mousepad">Kauko-ohjaus</string>
|
||||
<string name="mousepad_info">Siirrä hiirikohdistinta liikuttamalla sormea näytöllä. Tee hiirenpainallus napauttamalla, ja käytä kahta tai kolmea sormea oikealle ja keskipainikkeelle. Vieritä kahdella sormella. Pitkällä painalluksella voit vetää ja pudottaa. Gyrohiiritoiminnon voi ottaa käyttää liitännäisen asetuksista</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Paritettu laite ei tue näppäimistösyötettä</string>
|
||||
<string name="mousepad_single_tap_settings_title">Aseta yhden sormen napautuksen toiminto</string>
|
||||
<string name="mousepad_double_tap_settings_title">Aseta kahden sormen napautuksen toiminto</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Aseta kolmen sormen napautuksen toiminto</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Aseta kosketuslevyn herkkyys</string>
|
||||
<string name="mousepad_mouse_buttons_title">Näytä hiiripainikkeet</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Aseta osoittimen kiihdytys</string>
|
||||
<string name="mousepad_scroll_direction_title">Käänteinen vierityssuunta</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
@@ -245,7 +243,6 @@
|
||||
<string name="close">Sulje</string>
|
||||
<string name="plugins_need_permission">Jotkin liitännäiset vaativat toimiakseen lisäkäyttöoikeuksia (lisätietoa napsauttamalla):</string>
|
||||
<string name="permission_explanation">Liitännäinen tarvitsee toimiakseen lisäkäyttöoikeuksia</string>
|
||||
<string name="all_permissions_granted">Kaikki oikeudet myönnetty 🎉</string>
|
||||
<string name="optional_permission_explanation">Kaikkien toimintojen käyttämiseksi sinun on annettava lisäkäyttöoikeuksia</string>
|
||||
<string name="plugins_need_optional_permission">Jotkin liitännäisten ominaisuudet eivät ole käytössä puuttuvien käyttöoikeuksien takia (lisätietoa napsauttamalla):</string>
|
||||
<string name="share_optional_permission_explanation">Talletustilan käyttö on sallittava tiedostojen vastaanottamiseksi</string>
|
||||
|
@@ -51,7 +51,6 @@
|
||||
<string name="remotekeyboard_connected">La connexion au clavier sans fil est active</string>
|
||||
<string name="remotekeyboard_multiple_connections">Plusieurs connexions à des claviers sans fil sont disponibles, sélectionnez le périphérique à configurer</string>
|
||||
<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_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_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>
|
||||
@@ -60,8 +59,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">Afficher les boutons de souris</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Définir l\'accélération du pointeur</string>
|
||||
<string name="mousepad_scroll_direction_title">Inverser la direction du défilement</string>
|
||||
<string name="gyro_mouse_enabled_title">Activer la souris avec gyroscope</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Sensibilité du gyroscope</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Clic gauche</item>
|
||||
<item>Clic droit</item>
|
||||
@@ -108,7 +105,6 @@
|
||||
<string name="device_menu_plugins">Paramètres des modules externes</string>
|
||||
<string name="device_menu_unpair">Dissocier</string>
|
||||
<string name="pair_new_device">Associer un nouveau périphérique</string>
|
||||
<string name="cancel_pairing">Annuler la demande d\'association</string>
|
||||
<string name="unknown_device">Périphérique inconnu</string>
|
||||
<string name="error_not_reachable">Périphérique inaccessible</string>
|
||||
<string name="error_already_paired">Périphérique déjà associé</string>
|
||||
@@ -221,7 +217,7 @@
|
||||
<string name="sftp_action_mode_menu_delete">Supprimer</string>
|
||||
<string name="sftp_no_storage_locations_configured">Aucun emplacement stockage n\'est configuré</string>
|
||||
<string name="sftp_saf_permission_explanation">Vous devez configurer des emplacements de stockage pour accéder aux fichiers à distance</string>
|
||||
<string name="sftp_manage_storage_permission_explanation">Pour permettre l\'accès à distance aux fichiers sur ce périphérique, vous devez autoriser KDE Connect à gérer l\'enregistrement.</string>
|
||||
<string name="sftp_manage_storage_permission_explanation">Pour permettre l’accès à distance aux fichiers sur ce périphérique, vous devez autoriser KDE Connect à gérer l\'enregistrement.</string>
|
||||
<string name="no_players_connected">Aucun lecteur trouvé</string>
|
||||
<string name="send_files">Envoyer des fichiers</string>
|
||||
<string name="block_notification_contents">Bloquer les contenus de notifications</string>
|
||||
@@ -316,7 +312,7 @@
|
||||
<string name="empty_trusted_networks_list_text">Vous n\'avez pas encore ajouté de réseau de confiance</string>
|
||||
<string name="allow_all_networks_text">Tout autoriser</string>
|
||||
<string name="location_permission_needed_title">Permissions requises</string>
|
||||
<string name="location_permission_needed_desc">KDEConnect a besoin de la permission d\'accéder à la localisation en arrière-plan pour connaître le réseau Wifi sur lequel vous êtes connecté, même si l\'application est en arrière-plan. En effet, le nom des réseaux Wifi autour de vous pourrait être utilisé pour trouver votre emplacement, même si ce n\'est pas ce que KDEConnect fait.</string>
|
||||
<string name="location_permission_needed_desc">KDEConnect a besoin de la permission d\'accéder à la localisation en arrière-plan pour connaître le réseau Wifi sur lequel vous êtes connecté, même si l’application est en arrière-plan. En effet, le nom des réseaux Wifi autour de vous pourrait être utilisé pour trouver votre emplacement, même si ce n’est pas ce que KDEConnect fait.</string>
|
||||
<string name="clipboard_android_x_incompat">Android 10 a supprimé l\'accès des applications au presse-papier. Ce module externe sera désactivé.</string>
|
||||
<string name="mpris_open_url">Continuer la lecture ici</string>
|
||||
<string name="cant_open_url">Impossible d\'ouvrir l\'URL pour continuer la lecture</string>
|
||||
@@ -377,7 +373,6 @@
|
||||
<string name="click_here_to_type">Tapotez ici pour effectuer une saisie</string>
|
||||
<string name="clear_compose">Effacer</string>
|
||||
<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="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&gt. 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>
|
||||
|
@@ -51,17 +51,13 @@
|
||||
<string name="remotekeyboard_connected">A conexión de teclado remoto está activa.</string>
|
||||
<string name="remotekeyboard_multiple_connections">Hai máis dunha conexión de teclado remoto, seleccione o dispositivo para configurar.</string>
|
||||
<string name="open_mousepad">Entrada remota</string>
|
||||
<string name="mousepad_info">Mova un dedo na pantalla para mover o cursor do rato. Toque para facer clic, e use dous ou tres dedos para os botóns secundario e central. Use dous dedos para desprazar. Prema durante un tempo para arrastrar e soltar. A funcionalidade de rato de xiroscopio pode activarse desde a configuración do complemento.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">O dispositivo emparellado non permite entrada de teclado</string>
|
||||
<string name="mousepad_single_tap_settings_title">Definir a acción de tocar cun dedo</string>
|
||||
<string name="mousepad_double_tap_settings_title">Definir a acción de tocar con dous dedos</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Definir a acción de tocar con tres dedos</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Definir a sensibilidade do punteiro táctil</string>
|
||||
<string name="mousepad_mouse_buttons_title">Mostrar os botóns do rato</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Definir a aceleración do punteiro</string>
|
||||
<string name="mousepad_scroll_direction_title">Inverter a dirección de desprazamento</string>
|
||||
<string name="gyro_mouse_enabled_title">Activar o rato de xiroscopio</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Sensibilidade de xiroscopio</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Clic esquerdo</item>
|
||||
<item>Clic dereito</item>
|
||||
@@ -105,10 +101,9 @@
|
||||
<string name="category_connected_devices">Dispositivos conectados</string>
|
||||
<string name="category_not_paired_devices">Dispositivos dispoñíbeis</string>
|
||||
<string name="category_remembered_devices">Dispositivos coñecidos</string>
|
||||
<string name="device_menu_plugins">Configuración dos complementos</string>
|
||||
<string name="device_menu_plugins">Configuración do complemento</string>
|
||||
<string name="device_menu_unpair">Desemparellarse</string>
|
||||
<string name="pair_new_device">Emparellar cun novo dispositivo</string>
|
||||
<string name="cancel_pairing">Cancelar o emparellamento</string>
|
||||
<string name="unknown_device">Dispositivo descoñecido</string>
|
||||
<string name="error_not_reachable">Dispositivo fóra do alcance</string>
|
||||
<string name="error_already_paired">O dispositivo xa está emparellado.</string>
|
||||
@@ -295,7 +290,7 @@
|
||||
<string name="settings_rename">Nome do dispositivo</string>
|
||||
<string name="settings_dark_mode">Tema escuro</string>
|
||||
<string name="settings_more_settings_title">Máis opcións</string>
|
||||
<string name="settings_more_settings_text">As opcións específicas dun dispositivo están en «Configuración dos complementos» no dispositivo.</string>
|
||||
<string name="settings_more_settings_text">Pode atopar as opcións específicas dun dispositivo baixo «Configuración do complemento» desde un dispositivo.</string>
|
||||
<string name="setting_persistent_notification">Mostrar unha notificación persistente</string>
|
||||
<string name="setting_persistent_notification_oreo">Notificación persistente</string>
|
||||
<string name="setting_persistent_notification_description">Toque para activar ou desactivar na configuración de notificacións</string>
|
||||
@@ -377,7 +372,6 @@
|
||||
<string name="click_here_to_type">Toque aquí para escribir</string>
|
||||
<string name="clear_compose">Borrar</string>
|
||||
<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="about_kde_about">"<h1>Sobre</h1> <p>KDE é unha comunidade internacional de persoas adicadas á enxeñaría de software, á arte, á documentación, á tradución e á creación, todas elas comprometidas co desenvolvemento de <a href=https://www.gnu.org/philosophy/free-sw.html>software libre</a>. KDE produce o ambiente de escritorio Plasma, centos de aplicacións, e as moitas bibliotecas de software 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 software 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 software 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>
|
||||
|
@@ -185,7 +185,6 @@
|
||||
<string name="click_here_to_type">Tocca hic pro typar</string>
|
||||
<string name="clear_compose">Clara</string>
|
||||
<string name="send_compose">Invia</string>
|
||||
<string name="compose_send_title">Compone invia</string>
|
||||
<string name="open_compose_send">Compone texto</string>
|
||||
<string name="maintainer_and_developer">Mantenitor e developpator</string>
|
||||
<string name="developer">Disveloppator</string>
|
||||
|
@@ -51,17 +51,13 @@
|
||||
<string name="remotekeyboard_connected">La connessione della tastiera remota è attiva</string>
|
||||
<string name="remotekeyboard_multiple_connections">Ci sono più connessioni di tastiere remote, seleziona il dispositivo da configurare</string>
|
||||
<string name="open_mousepad">Impulso remoto</string>
|
||||
<string name="mousepad_info">Muovi un dito sullo schermo per spostare il puntatore del mouse. Tocca per un clic e usa due/tre dita per i pulsanti destro e centrale. Utilizza 2 dita per scorrere. Utilizza una pressione lunga per trascinare e rilasciare. È possibile abilitare la funzionalità del mouse giroscopico dalle preferenze dell\'estensione</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Immissione da tastiera non supportata dal dispositivo associato</string>
|
||||
<string name="mousepad_single_tap_settings_title">Imposta azione per il tocco a un dito</string>
|
||||
<string name="mousepad_double_tap_settings_title">Imposta azione per il tocco a due dita</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Imposta azione per il tocco a tre dita</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Imposta la sensibilità del touchpad</string>
|
||||
<string name="mousepad_mouse_buttons_title">Mostra i pulsanti del mouse</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Imposta accelerazione del puntatore</string>
|
||||
<string name="mousepad_scroll_direction_title">Inverti direzione di scorrimento</string>
|
||||
<string name="gyro_mouse_enabled_title">Abilita il mouse giroscopico</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Sensibilità del giroscopio</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Clic sinistro</item>
|
||||
<item>Clic destro</item>
|
||||
@@ -376,7 +372,6 @@
|
||||
<string name="click_here_to_type">Tocca qui per scrivere</string>
|
||||
<string name="clear_compose">Pulisci</string>
|
||||
<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="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>
|
||||
|
@@ -120,25 +120,25 @@
|
||||
<item quantity="other">%1$d ファイルを %2$s から受信しています</item>
|
||||
</plurals>
|
||||
<plurals name="incoming_files_text">
|
||||
<item quantity="other">(%3$d 個中 %2$d 個のファイル) : %1$s</item>
|
||||
<item quantity="other">ファイル: %1s</item>
|
||||
</plurals>
|
||||
<plurals name="outgoing_file_title">
|
||||
<item quantity="other">%1$d ファイルを %2$s へ送信中</item>
|
||||
</plurals>
|
||||
<plurals name="outgoing_files_text">
|
||||
<item quantity="other">(%3$d 個中 %2$d 個のファイル) : %1$s</item>
|
||||
<item quantity="other">ファイル: %1$s</item>
|
||||
</plurals>
|
||||
<plurals name="received_files_title">
|
||||
<item quantity="other">%1$s から %2$d ファイルを受信</item>
|
||||
<item quantity="other">%1$s からファイルを受信</item>
|
||||
</plurals>
|
||||
<plurals name="received_files_fail_title">
|
||||
<item quantity="other">%1$s からの %3$d 個中 %2$d 個のファイルの受信に失敗</item>
|
||||
<item quantity="other">%1$s からのファイルの受信に失敗</item>
|
||||
</plurals>
|
||||
<plurals name="sent_files_title">
|
||||
<item quantity="other">%1$s に %2$d 個のファイルを送信済み</item>
|
||||
<item quantity="other">%1$s にファイルを送信済み</item>
|
||||
</plurals>
|
||||
<plurals name="send_files_fail_title">
|
||||
<item quantity="other">%1$s への %3$d 個中 %2$d 個のファイル送信に失敗</item>
|
||||
<item quantity="other">%1$s へのファイル送信に失敗</item>
|
||||
</plurals>
|
||||
<string name="tap_to_open">タップして開く</string>
|
||||
<string name="received_file_text">タップして \'%1s\' を開く</string>
|
||||
|
@@ -43,11 +43,8 @@
|
||||
<string name="mousepad_double_tap_settings_title">ქმედება ორი თითით შეხებისას</string>
|
||||
<string name="mousepad_triple_tap_settings_title">ქმედება სამი თითით შეხებისას</string>
|
||||
<string name="mousepad_sensitivity_settings_title">დააყენეთ თაჩპედის მგრძნობიარობა</string>
|
||||
<string name="mousepad_mouse_buttons_title">თაგუნას ღილაკების ჩვენება</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">კურსორის აჩქარების დაყენება</string>
|
||||
<string name="mousepad_scroll_direction_title">აწევ-ჩამოწევის მიმართულების რევერსი</string>
|
||||
<string name="gyro_mouse_enabled_title">გიროსკოპის თაგუნას დაჩართვა</string>
|
||||
<string name="gyro_mouse_sensitivity_title">გიროსკოპის მგრძნობელობა</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>მარცხენა-წკაპი</item>
|
||||
<item>მარჯვენა წკაპი</item>
|
||||
@@ -69,7 +66,6 @@
|
||||
<item>ძლიერი</item>
|
||||
<item>უძლიერესი</item>
|
||||
</string-array>
|
||||
<string name="sendkeystrokes_sent_text">%1$s გაგზავნილია მოწყობილობაზე %2$s</string>
|
||||
<string name="sendkeystrokes_pref_category_title">სად გაიგზავნება ღილაკის დაჭერა</string>
|
||||
<string name="sendkeystrokes_pref_enabled">ღილაკის დაჭერის გაგზავნის ჩართვა</string>
|
||||
<string name="mouse_receiver_plugin_name">თაგუნას მიმღები</string>
|
||||
@@ -83,7 +79,6 @@
|
||||
<string name="device_menu_plugins">დამატების პარამეტრები</string>
|
||||
<string name="device_menu_unpair">დაწყვილების მოხსნა</string>
|
||||
<string name="pair_new_device">ახალი მოწყობილობის დაწყვილება</string>
|
||||
<string name="cancel_pairing">დაწყვილების გაუქმება</string>
|
||||
<string name="unknown_device">უცნობი მოწყობილობა</string>
|
||||
<string name="error_not_reachable">მოწყობილობა მიუწვდოელია</string>
|
||||
<string name="error_already_paired">მოწყობილობა უკვე დაწყვილებულია</string>
|
||||
@@ -279,7 +274,6 @@
|
||||
<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="maintainer_and_developer">პროგრამისტი და წამყვანი</string>
|
||||
<string name="developer">პროგრამისტი</string>
|
||||
|
@@ -51,13 +51,11 @@
|
||||
<string name="remotekeyboard_connected">원격 키보드 연결이 활성화됨</string>
|
||||
<string name="remotekeyboard_multiple_connections">원격 키보드 연결이 여러 개 있습니다. 설정할 장치를 선택하십시오</string>
|
||||
<string name="open_mousepad">원격 입력</string>
|
||||
<string name="mousepad_info">화면에서 손가락을 움직이면 마우스 커서를 움직입니다. 화면을 누르면 왼쪽 단추를 누르고, 두 손가락과 세 손가락으로 누르면 오른쪽/가운데 단추를 누릅니다. 두 손가락을 사용하여 스크롤할 수 있습니다. 드래그 앤 드롭을 사용하려면 길게 누르십시오. 플러그인 설정에서 자이로 마우스를 활성화할 수 있습니다</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>
|
||||
<string name="mousepad_triple_tap_settings_title">세 손가락으로 눌렀을 때 동작 설정</string>
|
||||
<string name="mousepad_sensitivity_settings_title">터치패드 감도 설정</string>
|
||||
<string name="mousepad_mouse_buttons_title">마우스 단추 표시</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">포인터 가속 설정</string>
|
||||
<string name="mousepad_scroll_direction_title">스크롤 방향 뒤집기</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
@@ -210,11 +208,8 @@
|
||||
<string name="sftp_action_mode_menu_delete">삭제</string>
|
||||
<string name="sftp_no_storage_locations_configured">저장소 위치가 설정되지 않았음</string>
|
||||
<string name="sftp_saf_permission_explanation">원격으로 파일에 접근하려면 저장소 위치를 설정해야 함</string>
|
||||
<string name="sftp_manage_storage_permission_explanation">이 장치에 있는 파일에 원격 접근을 허용하려면 KDE Connect에서 저장소를 관리할 수 있도록 허용해야 합니다.</string>
|
||||
<string name="no_players_connected">재생기를 찾을 수 없음</string>
|
||||
<string name="send_files">파일 보내기</string>
|
||||
<string name="block_notification_contents">알림 내용 숨기기</string>
|
||||
<string name="block_notification_images">알림 이미지 숨기기</string>
|
||||
<string name="pairing_title">KDE Connect 장치</string>
|
||||
<string name="pairing_description">같은 네트워크에서 KDE Connect를 실행하는 다른 장치가 여기에 표시됩니다.</string>
|
||||
<string name="device_rename_title">장치 이름 바꾸기</string>
|
||||
@@ -237,10 +232,8 @@
|
||||
<string name="close">닫기</string>
|
||||
<string name="plugins_need_permission">권한이 필요한 플러그인(정보를 보려면 누르기):</string>
|
||||
<string name="permission_explanation">이 플러그인을 사용하려면 권한이 필요합니다</string>
|
||||
<string name="all_permissions_granted">모든 권한 허가됨 🎉</string>
|
||||
<string name="optional_permission_explanation">모든 기능을 사용하려면 추가 권한이 필요합니다</string>
|
||||
<string name="plugins_need_optional_permission">일부 플러그인은 권한이 없어서 비활성화되었습니다(정보를 보려면 누르기):</string>
|
||||
<string name="share_optional_permission_explanation">공유된 파일을 받으려면 저장소 접근을 허가해야 합니다</string>
|
||||
<string name="telepathy_permission_explanation">데스크톱에서 문자 메시지를 읽고 보내려면 문자 메시지 접근 권한이 필요합니다</string>
|
||||
<string name="telephony_permission_explanation">데스크톱에서 통화와 문자 메시지를 보려면 통화 기록 및 휴대폰 상태 접근 권한이 필요합니다</string>
|
||||
<string name="telephony_optional_permission_explanation">전화번호 대신 연락처에 등록된 이름을 보려면 주소록 접근 권한이 필요합니다</string>
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<integer name="plugins_columns">3</integer>
|
||||
<integer name="mpris_now_playing_orientation">@integer/orientation_horizontal</integer>
|
||||
<integer name="mpris_now_playing_album_weight">1</integer>
|
||||
<integer name="mpris_now_playing_controls_weight">1</integer>
|
||||
|
@@ -11,6 +11,7 @@
|
||||
<color name="toolbar_color">@android:color/system_neutral1_900</color>
|
||||
<color name="card_stroke_color">@android:color/system_neutral2_800</color>
|
||||
<color name="activity_background">@android:color/system_neutral1_900</color>
|
||||
<item name="lightMode" type="bool">false</item>
|
||||
|
||||
<!-- This is for dark theme. In dark theme both selected and unselected text in the
|
||||
navigation bar should be white. This is different from the light theme as both states have
|
||||
|
@@ -11,6 +11,7 @@
|
||||
<color name="toolbar_color">@android:color/black</color>
|
||||
<color name="card_stroke_color">#8C8C8C</color>
|
||||
<color name="activity_background">@android:color/black</color>
|
||||
<item name="lightMode" type="bool">false</item>
|
||||
|
||||
<!-- This is for dark theme. In dark theme both selected and unselected text in the
|
||||
navigation bar should be white. This is different from the light theme as both states have
|
||||
|
@@ -60,8 +60,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">Muisknoppen tonen</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Aanwijzerversnelling instellen</string>
|
||||
<string name="mousepad_scroll_direction_title">Schuifrichting omdraaien</string>
|
||||
<string name="gyro_mouse_enabled_title">Gyroscoopmuis inschakelen</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Gyroscoopgevoeligheid</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Linker muisklik</item>
|
||||
<item>Rechter muisklik</item>
|
||||
@@ -108,7 +106,6 @@
|
||||
<string name="device_menu_plugins">Plugin-instellingen</string>
|
||||
<string name="device_menu_unpair">Paar uit elkaar halen</string>
|
||||
<string name="pair_new_device">Nieuw apparaat paren</string>
|
||||
<string name="cancel_pairing">Paren annuleren</string>
|
||||
<string name="unknown_device">Onbekend apparaat</string>
|
||||
<string name="error_not_reachable">Apparaat niet bereikbaar</string>
|
||||
<string name="error_already_paired">Apparaat is al gepaard</string>
|
||||
@@ -377,7 +374,6 @@
|
||||
<string name="click_here_to_type">Tik hier om te typen</string>
|
||||
<string name="clear_compose">Wissen</string>
|
||||
<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="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>
|
||||
|
@@ -51,17 +51,14 @@
|
||||
<string name="remotekeyboard_connected">Eksternt tastatursamband er verksamt</string>
|
||||
<string name="remotekeyboard_multiple_connections">Det finst meir enn eitt eksternt tastatursamband (vel eining å setja opp)</string>
|
||||
<string name="open_mousepad">Fjernstyring</string>
|
||||
<string name="mousepad_info">Dra ein finger over skjermen for å flytta peikaren på datamaskina. Trykk for å klikka, og bruk to eller tre fingrar for høvesvis høgre- og midtknappen. Bruk to fingrar for å rulla. Trykk lenge for å dra og sleppa. Funksjonalitet for gyromus kan du slå på i programtillegg-innstillingane.</string>
|
||||
<string name="mousepad_info">Dra ein finger over skjermen for å flytta peikaren på datamaskina. Trykk for å klikka, og bruk to eller tre fingrar for høvesvis høgre- og midtknappen. Bruk to fingrar for å rulla. Trykk lenge for å dra og sleppa.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Tekst frå tastaturet er ikkje støtta av den para eininga</string>
|
||||
<string name="mousepad_single_tap_settings_title">Vel handling for éinfingertrykk</string>
|
||||
<string name="mousepad_double_tap_settings_title">Vel handling for tofingertrykk</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Vel handling for trefingertrykk</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Vel følsemd for styreplate</string>
|
||||
<string name="mousepad_mouse_buttons_title">Vis museknappar</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Vel peikarakselerasjon</string>
|
||||
<string name="mousepad_scroll_direction_title">Omvend rulleretning</string>
|
||||
<string name="gyro_mouse_enabled_title">Slå på gyromus</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Gyroskop-følsemd</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Venstreklikk</item>
|
||||
<item>Høgreklikk</item>
|
||||
@@ -102,6 +99,7 @@
|
||||
<string name="battery_status_format">Batteri: %d %%</string>
|
||||
<string name="battery_status_low_format">Batteri: %d %% – lågt batterinivå</string>
|
||||
<string name="battery_status_charging_format">Batteri: %d %% – ladar</string>
|
||||
<string name="battery_status_unknown">Batteriinformasjon er ikkje tilgjengeleg</string>
|
||||
<string name="category_connected_devices">Tilkopla einingar</string>
|
||||
<string name="category_not_paired_devices">Tilgjengelege einingar</string>
|
||||
<string name="category_remembered_devices">Hugsa einingar</string>
|
||||
@@ -377,6 +375,7 @@
|
||||
<string name="clear_compose">Tøm</string>
|
||||
<string name="send_compose">Send</string>
|
||||
<string name="open_compose_send">Skriv tekst</string>
|
||||
<string name="app_description">Fleirplattforms-app for kommunikasjon på tvers av einingar (for eksempel mellom telefon og datamaskin)</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>
|
||||
|
@@ -11,5 +11,6 @@
|
||||
<color name="toolbar_color">@android:color/system_neutral1_50</color>
|
||||
<color name="card_stroke_color">@android:color/system_neutral2_100</color>
|
||||
<color name="activity_background">@android:color/system_neutral1_50</color>
|
||||
<item name="lightMode" type="bool">true</item>
|
||||
</resources>
|
||||
|
||||
|
@@ -51,7 +51,6 @@
|
||||
<string name="remotekeyboard_connected">Połączenie zdalnej klawiatury jest nawiązane</string>
|
||||
<string name="remotekeyboard_multiple_connections">Nawiązano więcej niż jedno połączenie zdalnej klawiatury, wybierz urządzenie do ustawienia</string>
|
||||
<string name="open_mousepad">Zdalne sterowanie</string>
|
||||
<string name="mousepad_info">Przesuwanie palcem po ekranie przesuwa wskaźnik myszy. Stuknięcie jednym, dwoma i trzema palcami wywołuje odpowiednio naciśnięcie lewym, prawym i środkowym przyciskiem myszy. Dwa palce przewijają. Długie naciśnięcie rozpoczyna czynność przeciągania i upuszczania. Zachowania żyroskopowe myszy mogą być włączone z poziomu ustawień wtyczki.</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Wpisywanie z klawiatury jest nieobsługiwane przez sparowane urządzenie</string>
|
||||
<string name="mousepad_single_tap_settings_title">Ustaw działanie po stuknięciu jednym palcem</string>
|
||||
<string name="mousepad_double_tap_settings_title">Ustaw działanie po stuknięciu dwoma palcami</string>
|
||||
@@ -60,8 +59,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">Pokaż przyciski myszy</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Ustaw przyspieszenie wskaźnika</string>
|
||||
<string name="mousepad_scroll_direction_title">Odwróć stronę przewijania</string>
|
||||
<string name="gyro_mouse_enabled_title">Włącz mysz żyroskopową</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Czułość żyroskopu</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Kliknięcie lewym</item>
|
||||
<item>Kliknięcie prawym</item>
|
||||
@@ -392,7 +389,6 @@
|
||||
<string name="click_here_to_type">Stuknij tutaj, aby wpisać</string>
|
||||
<string name="clear_compose">Wyczyść</string>
|
||||
<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="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>
|
||||
|
@@ -51,17 +51,11 @@
|
||||
<string name="remotekeyboard_connected">A conexão ao teclado remoto está ativa</string>
|
||||
<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_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>
|
||||
<string name="mousepad_triple_tap_settings_title">Definir ação do toque com três dedos</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Definir sensibilidade do touchpad</string>
|
||||
<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="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">
|
||||
<item>Clique esquerdo</item>
|
||||
<item>Clique direito</item>
|
||||
@@ -84,16 +78,13 @@
|
||||
<item>Muito forte</item>
|
||||
</string-array>
|
||||
<string name="sendkeystrokes_send_to">"Enviar pressionamento de teclas para "</string>
|
||||
<string name="sendkeystrokes_textbox_hint">Enviar pressionamento de teclas para o host</string>
|
||||
<string name="sendkeystrokes_disabled_toast">O envio de pressionamento de tecla está desabilitado - habilite-o nas configurações</string>
|
||||
<string name="sendkeystrokes_wrong_data">Tipo MIME inválido - precisa ser \'text/x-keystrokes\'</string>
|
||||
<string name="sendkeystrokes_sent_text">Enviar %1$s para o dispositivo %2$s</string>
|
||||
<string name="sendkeystrokes_pref_category_summary">Este módulo permite a outros aplicativos compartilhar segmentos de texto como pressionamento de teclas que serão enviados para o host conectado</string>
|
||||
<string name="sendkeystrokes_pref_category_title">Enviar pressionamento de teclas</string>
|
||||
<string name="sendkeystrokes_pref_enabled">Habilitar o envio de pressionamento de teclas</string>
|
||||
<string name="sendkeystrokes_pref_enabled_summary">Ouvir dados com o tipo MIME \'text/x-keystrokes\'</string>
|
||||
<string name="sendkeystrokes_safe_text_enabled">Enviar texto seguro imediatamente</string>
|
||||
<string name="sendkeystrokes_safe_text_enabled_summary">Enviar sequências numéricas curtas sem confirmação</string>
|
||||
<string name="pref_plugin_mousepad_send_keystrokes">Enviar como pressionamento de teclas</string>
|
||||
<string name="mouse_receiver_plugin_description">Receber movimento de mouse remoto</string>
|
||||
<string name="mouse_receiver_plugin_name">Receptor de mouse</string>
|
||||
@@ -220,11 +211,8 @@
|
||||
<string name="sftp_action_mode_menu_delete">Excluir</string>
|
||||
<string name="sftp_no_storage_locations_configured">Nenhum local de armazenamento configurado</string>
|
||||
<string name="sftp_saf_permission_explanation">Para acessar arquivos remotamente você precisa configurar locais de armazenamento</string>
|
||||
<string name="sftp_manage_storage_permission_explanation">Para permitir o acesso remoto aos arquivos neste dispositivo, você precisapermitir que o KDE Connect gerencie o armazenamento.</string>
|
||||
<string name="no_players_connected">Nenhum reprodutor encontrado</string>
|
||||
<string name="send_files">Enviar arquivos</string>
|
||||
<string name="block_notification_contents">Bloquear os conteúdos das notificações</string>
|
||||
<string name="block_notification_images">Bloquear as imagens das notificações</string>
|
||||
<string name="pairing_title">Dispositivos do KDE Connect</string>
|
||||
<string name="pairing_description">Outros dispositivos executando o KDE Connect na mesma rede devem aparecer aqui.</string>
|
||||
<string name="device_rename_title">Renomear dispositivo</string>
|
||||
@@ -247,10 +235,8 @@
|
||||
<string name="close">Fechar</string>
|
||||
<string name="plugins_need_permission">Alguns plugins precisam de permissões para funcionar (toque para mais informações):</string>
|
||||
<string name="permission_explanation">Este plugin precisa de permissões para funcionar</string>
|
||||
<string name="all_permissions_granted">Todas as permissões concedidas 🎉</string>
|
||||
<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="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>
|
||||
@@ -271,7 +257,6 @@
|
||||
<string name="pref_plugin_mprisreceiver">Controle do reprodutor de mídia</string>
|
||||
<string name="pref_plugin_mprisreceiver_desc">Controle os reprodutores de mídias do seu celular a partir de outro dispositivo</string>
|
||||
<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">Transferência de arquivo</string>
|
||||
<string name="notification_channel_high_priority">Prioridade alta</string>
|
||||
@@ -315,7 +300,6 @@
|
||||
<string name="empty_trusted_networks_list_text">Você ainda não adicionou nenhuma rede confiável</string>
|
||||
<string name="allow_all_networks_text">Permitir tudo</string>
|
||||
<string name="location_permission_needed_title">É necessário ter permissão</string>
|
||||
<string name="location_permission_needed_desc">O KDE Connect precisa da permissão de localização em segundo plano para conhecer a rede WiFi à qual você está conectado, mesmo quando o aplicativo está em segundo plano. Isso ocorre porque o nome da rede WiFi ao seu redor pode ser usado para encontrar sua localização, mesmo quando não é isso que o KDE Connect faz.</string>
|
||||
<string name="clipboard_android_x_incompat">O Android 10 removeu o acesso à área de transferência para todos os aplicativos. Este plugin ficará desativado.</string>
|
||||
<string name="mpris_open_url">Continuar tocando aqui</string>
|
||||
<string name="cant_open_url">Não foi possível abrir a URL para continuar tocando</string>
|
||||
@@ -363,7 +347,6 @@
|
||||
<string name="about">Sobre</string>
|
||||
<string name="authors">Autores</string>
|
||||
<string name="thanks_to">Agradecimentos a</string>
|
||||
<string name="easter_egg">Easter Egg</string>
|
||||
<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>
|
||||
@@ -371,17 +354,10 @@
|
||||
<string name="kde_be_free">KDE - Seja livre!</string>
|
||||
<string name="kde">KDE</string>
|
||||
<string name="konqi">Konqi</string>
|
||||
<string name="rise_up">Para cima</string>
|
||||
<string name="rise_down">Para baixo</string>
|
||||
<string name="click_here_to_type">Toque aqui para digitar</string>
|
||||
<string name="clear_compose">Limpar</string>
|
||||
<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_join_kde"><h1>Junte-se ao KDE</h1> <p> Você não precisa ser um desenvolvedor de software para ser membro da equipe do KDE. Você pode se juntar às equipes nacionais que traduzem interfaces de programas. Você pode fornecer gráficos, 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 nos quais você pode participar.</p> Se você precisar de mais informações ou documentação, uma visita a <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> fornecerá o que você precisa.</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="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>
|
||||
|
@@ -51,17 +51,13 @@
|
||||
<string name="remotekeyboard_connected">A ligação ao teclado remoto está activa</string>
|
||||
<string name="remotekeyboard_multiple_connections">Existe mais que uma ligação a teclados remotos; seleccione o dispositivo a configurar</string>
|
||||
<string name="open_mousepad">Introdução remota de dados</string>
|
||||
<string name="mousepad_info">Mova um dedo pelo ecrã para mover o cursor do rato. Dê um toque para carregar no botão esquerdo e use dois/três dedos para os botões direito e do meio. Use 2 dedos para deslocar-se. Use uma pressão longa para arrastar e largar. A funcionalidade giroscópica do rato pode ser activada a partir das preferências do \'plugin\'</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">O uso do teclado não é suportado pelo dispositivo emparelhado</string>
|
||||
<string name="mousepad_single_tap_settings_title">Definir a acção do toque com um dedo</string>
|
||||
<string name="mousepad_double_tap_settings_title">Definir a acção do toque com dois dedos</string>
|
||||
<string name="mousepad_triple_tap_settings_title">Definir a acção do toque com três dedos</string>
|
||||
<string name="mousepad_sensitivity_settings_title">Definir a sensibilidade do rato por toque</string>
|
||||
<string name="mousepad_mouse_buttons_title">Mostrar os botões do rato</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Definir a aceleração do cursor</string>
|
||||
<string name="mousepad_scroll_direction_title">Direcção de Deslocamento Inversa</string>
|
||||
<string name="gyro_mouse_enabled_title">Activar o rato com giroscópio</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Sensibilidade do giroscópio</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Botão esquerdo</item>
|
||||
<item>Botão direito</item>
|
||||
@@ -108,7 +104,6 @@
|
||||
<string name="device_menu_plugins">Configuração do \'plugin\'</string>
|
||||
<string name="device_menu_unpair">Desemparelhar</string>
|
||||
<string name="pair_new_device">Emparelhar um novo dispositivo</string>
|
||||
<string name="cancel_pairing">Cancelar o emparelhamento</string>
|
||||
<string name="unknown_device">Dispositivo desconhecido</string>
|
||||
<string name="error_not_reachable">Dispositivo inacessível</string>
|
||||
<string name="error_already_paired">O dispositivo já foi emparelhado</string>
|
||||
@@ -377,7 +372,6 @@
|
||||
<string name="click_here_to_type">Toque aqui para abrir</string>
|
||||
<string name="clear_compose">Limpar</string>
|
||||
<string name="send_compose">Enviar</string>
|
||||
<string name="compose_send_title">Compor o envio</string>
|
||||
<string name="open_compose_send">Compor o texto</string>
|
||||
<string name="about_kde_about"><h1>Acerca</h1> <p>O KDE é uma grande comunidade mundial de engenheiros de \'software\', artistas, 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 trabalho Plasma, centenas de aplicações e as diversas bibliotecas de \'software\' que dão suporte a elas.</p> <p>O KDE é uma empresa cooperativa: nenhuma entidade única controla a sua direcção ou produtos. Em vez disso, trabalhamos juntos para atingir o objectivo comum de criar o melhor Software Livre do mundo. Todos são bem-vindos para se <a href=https://community.kde.org/Get_Involved>juntarem e contribuírem</a> para o KDE, incluindo você mesmo.</p> Vá a <a href=https://www.kde.org/>https://www.kde.org/</a> para obter mais informações sobre a comunidade do KDE e as aplicações que produzimos.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Comunicar Erros ou Pedidos</h1> <p>O software pode ser sempre melhorado, e a equipa do KDE está preparada para o fazer. Contudo, você - o utilizador - deve--nos avisar quando algo não funciona como seria de esperar ou quando poderá ser feito de melhor maneira.</p> <p>O KDE tem um sistema de registo de erros. Vá a <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> ou use o botão \"Comunicar um Erro\" do ecrã \'Acerca\' para relatar erros.</p> Se tiver uma sugestão de melhorias, então é bem-vindo para usar o sistema de registo de erros para registar o seu pedido. Certifique-se que usa o tipo de criticidade \"Wishlist\" (Lista de Pedidos).</string>
|
||||
|
@@ -51,7 +51,6 @@
|
||||
<string name="remotekeyboard_connected">Povezava z oddaljeno tipkovnico je dejavna</string>
|
||||
<string name="remotekeyboard_multiple_connections">Obstaja več kot ena povezava na oddaljeno tipkovnico, izberite napravo in nastavitve</string>
|
||||
<string name="open_mousepad">Oddaljeni input</string>
|
||||
<string name="mousepad_info">Premaknite prst po zaslonu da bi premikali miško. Tapnite za klik in uporabite dva/tri prste za desni in srednji gumb. Za pomikanje uporabite dva prsta. Uporabite dolg pritisk za povleci in spusti. Žiroskopsko zmožnost miške lahko omogočite iz nastavitve vtičnika</string>
|
||||
<string name="mousepad_keyboard_input_not_supported">Vnos s tipkovnice ni podprt na sparjeni napravi</string>
|
||||
<string name="mousepad_single_tap_settings_title">Nastavite akcijo tapkanja enega prsta</string>
|
||||
<string name="mousepad_double_tap_settings_title">Nastavite akcijo tapkanja dveh prstov</string>
|
||||
@@ -60,8 +59,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">Prikaži miškine gumbe</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Nastavi pospeške kazalca</string>
|
||||
<string name="mousepad_scroll_direction_title">Smer povratnega drsenja</string>
|
||||
<string name="gyro_mouse_enabled_title">Omogoči žiroskopsko miško</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Občutljivost žiroskopa</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Levi klik</item>
|
||||
<item>Desni klik</item>
|
||||
@@ -108,7 +105,6 @@
|
||||
<string name="device_menu_plugins">Nastavitve vtičnikov</string>
|
||||
<string name="device_menu_unpair">Razkleni par</string>
|
||||
<string name="pair_new_device">Upari novo napravo</string>
|
||||
<string name="cancel_pairing">Prekliči uparjanje</string>
|
||||
<string name="unknown_device">Neznana naprava</string>
|
||||
<string name="error_not_reachable">Naprava ni dosegljiva</string>
|
||||
<string name="error_already_paired">Naprava je že uparjena</string>
|
||||
@@ -393,7 +389,6 @@
|
||||
<string name="click_here_to_type">Tapkajte, tukaj za tipkanje</string>
|
||||
<string name="clear_compose">Očisti</string>
|
||||
<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="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>
|
||||
|
@@ -51,7 +51,6 @@
|
||||
<string name="remotekeyboard_connected">தொலை விசைப்பலகை இணைப்பு செயலில் உள்ளது</string>
|
||||
<string name="remotekeyboard_multiple_connections">பல தொலை விசைப்பலகை இணைப்புகள் உள்ளன. அமைக்க வேண்டிய சாதனத்தை தேர்ந்தெடுங்கள்</string>
|
||||
<string name="open_mousepad">தொலை உள்ளீடு</string>
|
||||
<string name="mousepad_info">சுட்டிக்குறியை நகர்த்த ஒரு விரலை திரையில் நகர்த்தவும். \'க்ளிக்\' செய்வதற்கு தட்டுங்கள். வலது/நடு சுட்டி பட்டன்களுக்கு இரண்டு/மூன்று விரல்களை பயன்படுத்தவும். இரண்டு விரல்களைக் கொண்டு உருளவும். இழுத்து போடுவதற்கு நீண்ட அழுத்தத்தை பயன்படுத்தவும். சுழல்காட்டி சுட்டியைபோல் செயல்பட வேண்டுமெனில் செருகுநிரல் அமைப்புகளில் உரிய அம்சத்தை இயக்கலாம்</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>
|
||||
@@ -60,8 +59,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">சுட்டி பட்டன்களைக் காட்டு</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">சுட்டிக்குறியின் வேகவளர்ச்சியை அமை</string>
|
||||
<string name="mousepad_scroll_direction_title">உருளல் திசையை புரட்டு</string>
|
||||
<string name="gyro_mouse_enabled_title">சுழல்காட்டி சுட்டியை இயக்கு</string>
|
||||
<string name="gyro_mouse_sensitivity_title">சுழல்காட்டியின் உணர்வுத்திறம்</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>இடது கிளிக்</item>
|
||||
<item>வலது கிளிக்</item>
|
||||
@@ -373,7 +370,6 @@
|
||||
<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="about_kde_about">"<h1>பற்றி</h1> <p>கே.டீ.யீ. என்பது, <a href=https://www.gnu.org/philosophy/free-sw.html>கட்டற்ற மென்பொருள்</a> உருவாக்கத்திற்கு அர்பணிப்புக் கொண்ட மென்பொருள் பொறிஞர்கள், கலைஞர்கள், எழுத்தாளர்கள், மொழிபெயர்ப்பாளர்கள் மற்றும் படைப்பாலிகளைக் கொண்ட உலகளாவிய குழு ஆகும். பிளாஸ்மா பணிமேடை சூழல், நூற்றுக்கணக்கானசெயலிகள், மற்றும் அவற்றை ஆதரிக்கும் பல நிரலகங்களை கே.டீ.யீ. உருவாக்குகிறது.</p> <p>கே.டீ.யீ. ஒரு கூட்டுறவு அமைப்பாகும்: எந்தவொரு தனிப்பட்ட நிறுவனமோ நபரோ அதன் நோக்கத்தையோ படைப்புக்களையோ கட்டுப்படுத்துவதில்லை. கே.டீ.யீ.-யில் நீங்கள் உட்பட எவரேனும் <a href=https://community.kde.org/Get_Involved>இணைந்து பங்களிக்கலாம்</a>. </p> கே.டீ.யீ. சமூகத்தை பற்றியும் நாங்கள் உருவாக்கும் மென்பொருட்களை பற்றியும் அறிய <a href=https://www.kde.org/>https://www.kde.org/</a> என்ற பக்கத்தை அணுகுங்கள்."</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>பிழைகளையோ விருப்பங்களையோ தாக்கல் செய்யுங்கள்</h1> <p>எந்த மென்பொருளும் மேம்படுத்த தக்கதே. கே.டீ.யீ. குழு அதனைச் செய்ய தயாராக உள்ளது. ஆயினும் பயனராகிய நீங்கள், எதிர்பார்த்த படி பணிசெய்யாதவை குறித்தும், இன்னும் சிறப்பாகச் செய்யக்கூடியவை குறித்தும் எங்களுக்கு தெரியப்படுத்த வேண்டும். </p> <p>கே.டீ.யீ.க்கு, பிழைகளை கண்காணிக்கும் அமைப்பொன்று உள்ளது. <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> என்ற பக்கத்தை அணுகவும், அல்லது \"உதவி\" பட்டியிலுள்ள \"பிழையைத் தெரிவி...\" என்ற சாளரத்தை பயன்படுத்தவும்.</p> நீங்கள் விரும்பும் மாற்றங்களை பரிந்துரைக்கக் கூட பிழைகளை கண்காணிக்கும் அமைப்பினைப் பயன்படுத்தலாம். அப்படி தெரிவிக்கும்போது, \"Wishlist\" என்ற முக்கியத்துவத்தை தேர்ந்தெடுங்கள்.</string>
|
||||
|
@@ -60,8 +60,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">Fare düğmelerini göster</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">İşaretçi ivmesini ayarla</string>
|
||||
<string name="mousepad_scroll_direction_title">Sarma Yönünü Tersine Çevir</string>
|
||||
<string name="gyro_mouse_enabled_title">Jiroskop fareyi etkinleştir</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Jiroskop hassasiyeti</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Sol tık</item>
|
||||
<item>Sağ tık</item>
|
||||
@@ -104,11 +102,10 @@
|
||||
<string name="battery_status_charging_format">Pil: %d%% Şarj oluyor</string>
|
||||
<string name="category_connected_devices">Bağlı aygıtlar</string>
|
||||
<string name="category_not_paired_devices">Kullanılabilir aygıtlar</string>
|
||||
<string name="category_remembered_devices">Anımsanan aygıtlar</string>
|
||||
<string name="category_remembered_devices">Hatırlanan aygıtlar</string>
|
||||
<string name="device_menu_plugins">Eklenti ayarları</string>
|
||||
<string name="device_menu_unpair">Ayır</string>
|
||||
<string name="pair_new_device">Yeni bir aygıt eşleştir</string>
|
||||
<string name="cancel_pairing">Eşleşmeyi İptal Et</string>
|
||||
<string name="unknown_device">Bİlinmeyen aygıt</string>
|
||||
<string name="error_not_reachable">Aygıt ulaşılabilir değil</string>
|
||||
<string name="error_already_paired">Aygıt zaten eşleşmiş</string>
|
||||
@@ -377,7 +374,6 @@
|
||||
<string name="click_here_to_type">Yazmak için buraya dokunun</string>
|
||||
<string name="clear_compose">Temiz</string>
|
||||
<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="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şbirlikçi bir kurumdur: Tek bir varlık yönünü veya ürünlerini kontrol etmez. Bunun yerine, dünyanın en kaliteli Özgür Yazılım\'larını üretme hedefi için birlikte çalışırız. Herkes, sen de dahil olmak üzere, KDE\'ye <a href=https://community.kde.org/Get_Involved>katılıp katkıda bulunmakta özgürdür</a>.</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ım her zaman iyileştirilebilir ve KDE takımın bunu yapmaya hazı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\" ciddiyet düzeyini kullandığınızdan emin olun.</string>
|
||||
|
@@ -60,8 +60,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">Показати кнопки миші</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">Встановити прискорення вказівника</string>
|
||||
<string name="mousepad_scroll_direction_title">Зворотний напрямок гортання</string>
|
||||
<string name="gyro_mouse_enabled_title">Увімкнути гіроскопічну мишу</string>
|
||||
<string name="gyro_mouse_sensitivity_title">Чутливість гіроскопа</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Клацання лівою</item>
|
||||
<item>Клацання правою</item>
|
||||
@@ -108,7 +106,6 @@
|
||||
<string name="device_menu_plugins">Параметри додатків</string>
|
||||
<string name="device_menu_unpair">Скасувати пов’язування</string>
|
||||
<string name="pair_new_device">Пов’язати новий пристрій</string>
|
||||
<string name="cancel_pairing">Скасувати пов\'язування</string>
|
||||
<string name="unknown_device">Невідомий пристрій</string>
|
||||
<string name="error_not_reachable">Немає доступу до пристрою</string>
|
||||
<string name="error_already_paired">Пристрій вже пов’язано</string>
|
||||
@@ -393,7 +390,6 @@
|
||||
<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="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>
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
<style name="KdeConnectTheme.NoActionBar" parent="KdeConnectThemeBase.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
|
||||
<item name="android:windowLightStatusBar">@bool/lightMode</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="KdeConnectThemeBase.V27" parent="KdeConnectThemeBase">
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@color/activity_background</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:windowLightNavigationBar">?attr/isLightTheme</item>
|
||||
</style>
|
||||
|
@@ -51,7 +51,6 @@
|
||||
<string name="remotekeyboard_connected">远程键盘连接已启用</string>
|
||||
<string name="remotekeyboard_multiple_connections">发现多个远程键盘连接,请选择要配置的设备</string>
|
||||
<string name="open_mousepad">远程输入</string>
|
||||
<string name="mousepad_info">在屏幕上移动手指来移动光标。轻击代表左键,双指或三指点击代表右键或中键。用双指滚动。用长按来拖放。基于陀螺仪的空中鼠标功能可以在插件的首选项中启用。</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>
|
||||
@@ -60,8 +59,6 @@
|
||||
<string name="mousepad_mouse_buttons_title">显示鼠标按钮</string>
|
||||
<string name="mousepad_acceleration_profile_settings_title">设置指针加速度</string>
|
||||
<string name="mousepad_scroll_direction_title">反转滚动方向</string>
|
||||
<string name="gyro_mouse_enabled_title">启用陀螺仪鼠标</string>
|
||||
<string name="gyro_mouse_sensitivity_title">陀螺仪灵敏度</string>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>左键点击</item>
|
||||
<item>右键点击</item>
|
||||
@@ -368,7 +365,6 @@
|
||||
<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="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>
|
||||
|
@@ -4,11 +4,12 @@
|
||||
<color name="primaryDark">@android:color/white</color>
|
||||
<color name="accent">#F67400</color>
|
||||
<color name="disabled_grey">#EEEEEE</color>
|
||||
<color name="on_secondary">#C8C8C8</color>
|
||||
<color name="on_secondary">@android:color/white</color>
|
||||
<color name="on_high_contrast">@android:color/black</color>
|
||||
<color name="text_color_primary">@android:color/black</color>
|
||||
<color name="text_color">@android:color/black</color>
|
||||
<color name="toolbar_color">@android:color/white</color>
|
||||
<color name="card_stroke_color">#C8C8C8</color>
|
||||
<color name="activity_background">@android:color/white</color>
|
||||
<item name="lightMode" type="bool">true</item>
|
||||
</resources>
|
@@ -75,9 +75,6 @@
|
||||
<string name="mousepad_scroll_direction" translatable="false">mousepad_scroll_direction</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="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>
|
||||
<string-array name="mousepad_tap_entries">
|
||||
<item>Left click</item>
|
||||
<item>Right click</item>
|
||||
@@ -160,7 +157,6 @@
|
||||
<string name="device_menu_plugins">Plugin settings</string>
|
||||
<string name="device_menu_unpair">Unpair</string>
|
||||
<string name="pair_new_device">Pair new device</string>
|
||||
<string name="cancel_pairing">Cancel pairing</string>
|
||||
<string name="unknown_device">Unknown device</string>
|
||||
<string name="error_not_reachable">Device not reachable</string>
|
||||
<string name="error_already_paired">Device already paired</string>
|
||||
@@ -489,7 +485,6 @@
|
||||
<string name="click_here_to_type">Tap here to type</string>
|
||||
<string name="clear_compose">Clear</string>
|
||||
<string name="send_compose">Send</string>
|
||||
<string name="compose_send_title">Compose send</string>
|
||||
<string name="open_compose_send">Compose text</string>
|
||||
|
||||
<string name="about_kde_about"><![CDATA[
|
||||
@@ -538,6 +533,4 @@
|
||||
<string name="send_clipboard">Send clipboard</string>
|
||||
<string name="tap_to_execute">Tap to execute</string>
|
||||
|
||||
<string name="plugin_stats">Plugin stats</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -2,24 +2,25 @@
|
||||
<!-- NoActionBar because we use a Toolbar widget as ActionBar -->
|
||||
<style name="KdeConnectThemeBase" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- The main color attributes -->
|
||||
<!-- The three colors used by system widgets, according to https://chris.banes.me/2014/10/17/appcompat-v21/ -->
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||
<item name="colorSecondary">@color/primary</item>
|
||||
<item name="colorOnSecondary">@color/on_secondary</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
<item name="colorHighContrast">@color/on_high_contrast</item>
|
||||
<item name="android:windowBackground">@color/activity_background</item>
|
||||
<item name="android:colorBackground">@color/activity_background</item>
|
||||
<!-- TODO: The 2 items below change too much (eg snackbar text is now black, should be white) -->
|
||||
<item name="android:textColorPrimary">@color/text_color_primary</item>
|
||||
<item name="android:textColor">@color/text_color</item>
|
||||
|
||||
<!--For android below 23 api-->
|
||||
<item name="android:statusBarColor">@android:color/black</item>
|
||||
|
||||
<!-- Drawable definitions and overrides -->
|
||||
<item name="divider">?colorHighContrast</item>
|
||||
|
||||
<!-- Style overrides -->
|
||||
<item name="actionModeStyle">@style/Widget.Material3.ActionMode</item>
|
||||
<item name="toolbarStyle">@style/Widget.Material3.Toolbar</item>
|
||||
<item name="actionModeStyle">@style/ActionModeStyle</item>
|
||||
<item name="toolbarStyle">@style/KdeConnectTheme.Toolbar</item>
|
||||
|
||||
<!-- Theme overrides -->
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
|
||||
|
@@ -61,14 +61,7 @@
|
||||
android:id="@+id/gyro_mouse_enabled"
|
||||
android:defaultValue="false"
|
||||
android:key="@string/gyro_mouse_enabled"
|
||||
android:title="@string/gyro_mouse_enabled_title" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:id="@+id/mousepad_gyro_sensitivity"
|
||||
android:defaultValue="100"
|
||||
android:key="@string/gyro_mouse_sensitivity"
|
||||
android:title="@string/gyro_mouse_sensitivity_title"
|
||||
android:layout_width="wrap_content" />
|
||||
android:title="Gyro mouse" />
|
||||
|
||||
<SwitchPreference
|
||||
android:id="@+id/mousepad_mouse_buttons_enabled_pref"
|
||||
|
@@ -8,53 +8,64 @@ package org.kde.kdeconnect.Backends;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public abstract class BaseLink {
|
||||
|
||||
protected final Context context;
|
||||
|
||||
public interface PacketReceiver {
|
||||
void onPacketReceived(@NonNull NetworkPacket np);
|
||||
void onPacketReceived(NetworkPacket np);
|
||||
}
|
||||
|
||||
protected final Context context;
|
||||
private final BaseLinkProvider linkProvider;
|
||||
private final String deviceId;
|
||||
private final ArrayList<PacketReceiver> receivers = new ArrayList<>();
|
||||
protected PrivateKey privateKey;
|
||||
|
||||
protected BaseLink(@NonNull Context context, @NonNull String deviceId, @NonNull BaseLinkProvider linkProvider) {
|
||||
this.context = context;
|
||||
protected BaseLink(Context context, String deviceId, BaseLinkProvider linkProvider) {
|
||||
this.context = context;
|
||||
this.linkProvider = linkProvider;
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
/* To be implemented by each link for pairing handlers */
|
||||
public abstract String getName();
|
||||
public abstract BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback);
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setPrivateKey(PrivateKey key) {
|
||||
privateKey = key;
|
||||
}
|
||||
|
||||
public BaseLinkProvider getLinkProvider() {
|
||||
return linkProvider;
|
||||
}
|
||||
|
||||
public void addPacketReceiver(@NonNull PacketReceiver pr) {
|
||||
//The daemon will periodically destroy unpaired links if this returns false
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPacketReceiver(PacketReceiver pr) {
|
||||
receivers.add(pr);
|
||||
}
|
||||
public void removePacketReceiver(@NonNull PacketReceiver pr) {
|
||||
public void removePacketReceiver(PacketReceiver pr) {
|
||||
receivers.remove(pr);
|
||||
}
|
||||
|
||||
//Should be called from a background thread listening for packets
|
||||
protected void packetReceived(@NonNull NetworkPacket np) {
|
||||
protected void packetReceived(NetworkPacket np) {
|
||||
for(PacketReceiver pr : receivers) {
|
||||
pr.onPacketReceived(np);
|
||||
}
|
||||
@@ -64,7 +75,7 @@ public abstract class BaseLink {
|
||||
linkProvider.connectionLost(this);
|
||||
}
|
||||
|
||||
//TO OVERRIDE, should be sync. If sendPayloadFromSameThread is false, it should only block to send the packet but start a separate thread to send the payload.
|
||||
//TO OVERRIDE, should be sync
|
||||
@WorkerThread
|
||||
public abstract boolean sendPacket(@NonNull NetworkPacket np, @NonNull Device.SendPacketStatusCallback callback, boolean sendPayloadFromSameThread) throws IOException;
|
||||
public abstract boolean sendPacket(NetworkPacket np, Device.SendPacketStatusCallback callback);
|
||||
}
|
||||
|
@@ -6,11 +6,8 @@
|
||||
|
||||
package org.kde.kdeconnect.Backends;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public abstract class BaseLinkProvider {
|
||||
@@ -18,10 +15,7 @@ public abstract class BaseLinkProvider {
|
||||
private final CopyOnWriteArrayList<ConnectionReceiver> connectionReceivers = new CopyOnWriteArrayList<>();
|
||||
|
||||
public interface ConnectionReceiver {
|
||||
void onConnectionReceived(@NonNull final String deviceId,
|
||||
@NonNull final Certificate certificate,
|
||||
@NonNull final NetworkPacket identityPacket,
|
||||
@NonNull final BaseLink link);
|
||||
void onConnectionReceived(NetworkPacket identityPacket, BaseLink link);
|
||||
void onConnectionLost(BaseLink link);
|
||||
}
|
||||
|
||||
@@ -34,13 +28,10 @@ public abstract class BaseLinkProvider {
|
||||
}
|
||||
|
||||
//These two should be called when the provider links to a new computer
|
||||
protected void connectionAccepted(@NonNull final String deviceId,
|
||||
@NonNull final Certificate certificate,
|
||||
@NonNull final NetworkPacket identityPacket,
|
||||
@NonNull final BaseLink link) {
|
||||
protected void connectionAccepted(NetworkPacket identityPacket, BaseLink link) {
|
||||
//Log.i("KDE/LinkProvider", "connectionAccepted");
|
||||
for(ConnectionReceiver cr : connectionReceivers) {
|
||||
cr.onConnectionReceived(deviceId, certificate, identityPacket, link);
|
||||
cr.onConnectionReceived(identityPacket, link);
|
||||
}
|
||||
}
|
||||
protected void connectionLost(BaseLink link) {
|
||||
@@ -54,6 +45,8 @@ public abstract class BaseLinkProvider {
|
||||
public abstract void onStart();
|
||||
public abstract void onStop();
|
||||
public abstract void onNetworkChange();
|
||||
|
||||
//public abstract int getPriority();
|
||||
public abstract String getName();
|
||||
|
||||
}
|
||||
|
68
src/org/kde/kdeconnect/Backends/BasePairingHandler.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Backends;
|
||||
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
/**
|
||||
* This class separates the pairing interface for each type of link.
|
||||
* Since different links can pair via different methods, like for LanLink certificate and public key should be shared,
|
||||
* for Bluetooth link they should be paired via bluetooth etc.
|
||||
* Each "Device" instance maintains a hash map for these pairing handlers so that there can be single pairing handler per
|
||||
* per link type per device.
|
||||
* Pairing handler keeps information about device, latest link, and pair status of the link
|
||||
* During first pairing process, the pairing process is nearly same as old process.
|
||||
* After that if any one of the link is paired, then we can say that device is paired, so new link will pair automatically
|
||||
*/
|
||||
|
||||
public abstract class BasePairingHandler {
|
||||
|
||||
protected enum PairStatus{
|
||||
NotPaired,
|
||||
Requested,
|
||||
RequestedByPeer,
|
||||
Paired
|
||||
}
|
||||
|
||||
public interface PairingHandlerCallback {
|
||||
void incomingRequest();
|
||||
void pairingDone();
|
||||
void pairingFailed(String error);
|
||||
void unpaired();
|
||||
}
|
||||
|
||||
|
||||
protected final Device mDevice;
|
||||
protected PairStatus mPairStatus;
|
||||
protected final PairingHandlerCallback mCallback;
|
||||
|
||||
protected BasePairingHandler(Device device, PairingHandlerCallback callback) {
|
||||
this.mDevice = device;
|
||||
this.mCallback = callback;
|
||||
}
|
||||
|
||||
protected boolean isPaired() {
|
||||
return mPairStatus == PairStatus.Paired;
|
||||
}
|
||||
|
||||
public boolean isPairRequested() {
|
||||
return mPairStatus == PairStatus.Requested;
|
||||
}
|
||||
|
||||
public boolean isPairRequestedByPeer() {
|
||||
return mPairStatus == PairStatus.RequestedByPeer;
|
||||
}
|
||||
|
||||
/* To be implemented by respective pairing handler */
|
||||
public abstract void packetReceived(NetworkPacket np);
|
||||
public abstract void requestPairing();
|
||||
public abstract void acceptPairing();
|
||||
public abstract void rejectPairing();
|
||||
public abstract void unpair();
|
||||
|
||||
}
|
@@ -10,12 +10,12 @@ import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BasePairingHandler;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
@@ -111,6 +111,11 @@ public class BluetoothLink extends BaseLink {
|
||||
return "BluetoothLink";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) {
|
||||
return new BluetoothPairingHandler(device, callback);
|
||||
}
|
||||
|
||||
public void disconnect() {
|
||||
if (connection == null) {
|
||||
return;
|
||||
@@ -132,8 +137,7 @@ public class BluetoothLink extends BaseLink {
|
||||
|
||||
@WorkerThread
|
||||
@Override
|
||||
public boolean sendPacket(@NonNull NetworkPacket np, @NonNull Device.SendPacketStatusCallback callback, boolean sendPayloadFromSameThread) throws IOException {
|
||||
// sendPayloadFromSameThread is ignored, we always send from the same thread!
|
||||
public boolean sendPacket(NetworkPacket np, final Device.SendPacketStatusCallback callback) {
|
||||
|
||||
/*if (!isConnected()) {
|
||||
Log.e("BluetoothLink", "sendPacketEncrypted failed: not connected");
|
||||
@@ -164,7 +168,7 @@ public class BluetoothLink extends BaseLink {
|
||||
progress += bytesRead;
|
||||
payloadStream.write(buffer, 0, bytesRead);
|
||||
if (np.getPayloadSize() > 0) {
|
||||
callback.onPayloadProgressChanged((int) (100 * progress / np.getPayloadSize()));
|
||||
callback.onProgressChanged((int) (100 * progress / np.getPayloadSize()));
|
||||
}
|
||||
}
|
||||
payloadStream.flush();
|
||||
@@ -182,6 +186,11 @@ public class BluetoothLink extends BaseLink {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
return receivingThread.isAlive();
|
||||
}
|
||||
|
||||
/*
|
||||
public boolean isConnected() {
|
||||
return socket.isConnected();
|
||||
|
@@ -15,12 +15,10 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
@@ -29,8 +27,6 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -52,12 +48,8 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
private ServerRunnable serverRunnable;
|
||||
private ClientRunnable clientRunnable;
|
||||
|
||||
private void addLink(NetworkPacket identityPacket, BluetoothLink link) throws CertificateException {
|
||||
private void addLink(NetworkPacket identityPacket, BluetoothLink link) {
|
||||
String deviceId = identityPacket.getString("deviceId");
|
||||
String certificateString = identityPacket.getString("certificate");
|
||||
byte[] certificateBytes = Base64.decode(certificateString, 0);
|
||||
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
|
||||
|
||||
Log.i("BluetoothLinkProvider", "addLink to " + deviceId);
|
||||
BluetoothLink oldLink = visibleComputers.get(deviceId);
|
||||
if (oldLink == link) {
|
||||
@@ -65,7 +57,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
return;
|
||||
}
|
||||
visibleComputers.put(deviceId, link);
|
||||
connectionAccepted(deviceId, certificate, identityPacket, link);
|
||||
connectionAccepted(identityPacket, link);
|
||||
link.startListening();
|
||||
if (oldLink != null) {
|
||||
Log.i("BluetoothLinkProvider", "Removing old connection to same device");
|
||||
@@ -197,7 +189,6 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
InputStream inputStream = connection.getDefaultInputStream();
|
||||
|
||||
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
|
||||
np.set("certificate", Base64.encodeToString(SslHelper.certificate.getEncoded(), 0));
|
||||
byte[] message = np.serialize().getBytes(Charsets.UTF_8);
|
||||
outputStream.write(message);
|
||||
outputStream.flush();
|
||||
@@ -380,18 +371,14 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
link.sendPacket(np2, new Device.SendPacketStatusCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
try {
|
||||
addLink(identityPacket, link);
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
addLink(identityPacket, link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("BTLinkProvider/Client", "Connection lost/disconnected on " + device.getAddress(), e);
|
||||
}
|
||||
|
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Backends.BluetoothBackend;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BasePairingHandler;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class BluetoothPairingHandler extends BasePairingHandler {
|
||||
|
||||
private Timer mPairingTimer;
|
||||
|
||||
public BluetoothPairingHandler(Device device, final PairingHandlerCallback callback) {
|
||||
super(device, callback);
|
||||
|
||||
if (device.isPaired()) {
|
||||
mPairStatus = PairStatus.Paired;
|
||||
} else {
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
private NetworkPacket createPairPacket() {
|
||||
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_PAIR);
|
||||
np.set("pair", true);
|
||||
return np;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(NetworkPacket np) {
|
||||
|
||||
boolean wantsPair = np.getBoolean("pair");
|
||||
|
||||
if (wantsPair == isPaired()) {
|
||||
if (mPairStatus == PairStatus.Requested) {
|
||||
//Log.e("Device","Unpairing (pair rejected)");
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
hidePairingNotification();
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (wantsPair) {
|
||||
|
||||
if (mPairStatus == PairStatus.Requested) { //We started pairing
|
||||
hidePairingNotification();
|
||||
pairingDone();
|
||||
} else {
|
||||
|
||||
// If device is already paired, accept pairing silently
|
||||
if (mDevice.isPaired()) {
|
||||
acceptPairing();
|
||||
return;
|
||||
}
|
||||
|
||||
// Pairing notifications are still managed by device as there is no other way to
|
||||
// know about notificationId to cancel notification when PairActivity is started
|
||||
// Even putting notificationId in intent does not work because PairActivity can be
|
||||
// started from MainActivity too, so then notificationId cannot be set
|
||||
hidePairingNotification();
|
||||
mDevice.displayPairingNotification();
|
||||
|
||||
mPairingTimer = new Timer();
|
||||
|
||||
mPairingTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.w("KDE/Device", "Unpairing (timeout B)");
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
hidePairingNotification();
|
||||
}
|
||||
}, 25 * 1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
|
||||
mPairStatus = PairStatus.RequestedByPeer;
|
||||
mCallback.incomingRequest();
|
||||
|
||||
}
|
||||
} else {
|
||||
Log.i("KDE/Pairing", "Unpair request");
|
||||
|
||||
if (mPairStatus == PairStatus.Requested) {
|
||||
hidePairingNotification();
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
|
||||
} else if (mPairStatus == PairStatus.Paired) {
|
||||
mCallback.unpaired();
|
||||
}
|
||||
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPairing() {
|
||||
|
||||
Device.SendPacketStatusCallback statusCallback = new Device.SendPacketStatusCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
hidePairingNotification(); //Will stop the pairingTimer if it was running
|
||||
mPairingTimer = new Timer();
|
||||
mPairingTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_timed_out));
|
||||
Log.w("KDE/Device", "Unpairing (timeout A)");
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
}
|
||||
}, 30 * 1000); //Time to wait for the other to accept
|
||||
mPairStatus = PairStatus.Requested;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.runcommand_notreachable));
|
||||
}
|
||||
};
|
||||
mDevice.sendPacket(createPairPacket(), statusCallback);
|
||||
}
|
||||
|
||||
private void hidePairingNotification() {
|
||||
mDevice.hidePairingNotification();
|
||||
if (mPairingTimer != null) {
|
||||
mPairingTimer.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptPairing() {
|
||||
hidePairingNotification();
|
||||
Device.SendPacketStatusCallback statusCallback = new Device.SendPacketStatusCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
pairingDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_not_reachable));
|
||||
}
|
||||
};
|
||||
mDevice.sendPacket(createPairPacket(), statusCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectPairing() {
|
||||
hidePairingNotification();
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_PAIR);
|
||||
np.set("pair", false);
|
||||
mDevice.sendPacket(np);
|
||||
}
|
||||
|
||||
//@Override
|
||||
private void pairingDone() {
|
||||
// Store device information needed to create a Device object in a future
|
||||
//Log.e("KDE/PairingDone", "Pairing Done");
|
||||
mPairStatus = PairStatus.Paired;
|
||||
mCallback.pairingDone();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpair() {
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_PAIR);
|
||||
np.set("pair", false);
|
||||
mDevice.sendPacket(np);
|
||||
}
|
||||
}
|
@@ -9,11 +9,11 @@ package org.kde.kdeconnect.Backends.LanBackend;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BasePairingHandler;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
@@ -45,6 +45,11 @@ public class LanLink extends BaseLink {
|
||||
Locally, Remotely
|
||||
}
|
||||
|
||||
private ConnectionStarted connectionSource; // If the other device sent me a broadcast,
|
||||
// I should not close the connection with it
|
||||
// because it's probably trying to find me and
|
||||
// potentially ask for pairing.
|
||||
|
||||
private volatile SSLSocket socket = null;
|
||||
|
||||
private final LinkDisconnectedCallback callback;
|
||||
@@ -60,11 +65,13 @@ public class LanLink extends BaseLink {
|
||||
}
|
||||
|
||||
//Returns the old socket
|
||||
public SSLSocket reset(final SSLSocket newSocket) throws IOException {
|
||||
public SSLSocket reset(final SSLSocket newSocket, ConnectionStarted connectionSource) throws IOException {
|
||||
|
||||
SSLSocket oldSocket = socket;
|
||||
socket = newSocket;
|
||||
|
||||
this.connectionSource = connectionSource;
|
||||
|
||||
if (oldSocket != null) {
|
||||
oldSocket.close(); //This should cancel the readThread
|
||||
}
|
||||
@@ -104,10 +111,10 @@ public class LanLink extends BaseLink {
|
||||
return oldSocket;
|
||||
}
|
||||
|
||||
public LanLink(Context context, String deviceId, LanLinkProvider linkProvider, SSLSocket socket) throws IOException {
|
||||
public LanLink(Context context, String deviceId, LanLinkProvider linkProvider, SSLSocket socket, ConnectionStarted connectionSource) throws IOException {
|
||||
super(context, deviceId, linkProvider);
|
||||
callback = linkProvider;
|
||||
reset(socket);
|
||||
reset(socket, connectionSource);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,10 +123,15 @@ public class LanLink extends BaseLink {
|
||||
return "LanLink";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) {
|
||||
return new LanPairingHandler(device, callback);
|
||||
}
|
||||
|
||||
//Blocking, do not call from main thread
|
||||
@WorkerThread
|
||||
@Override
|
||||
public boolean sendPacket(@NonNull NetworkPacket np, @NonNull final Device.SendPacketStatusCallback callback, boolean sendPayloadFromSameThread) {
|
||||
public boolean sendPacket(NetworkPacket np, final Device.SendPacketStatusCallback callback) {
|
||||
if (socket == null) {
|
||||
Log.e("KDE/sendPacket", "Not yet connected");
|
||||
callback.onFailure(new NotYetConnectedException());
|
||||
@@ -153,17 +165,51 @@ public class LanLink extends BaseLink {
|
||||
|
||||
//Send payload
|
||||
if (server != null) {
|
||||
if (sendPayloadFromSameThread) {
|
||||
sendPayload(np, callback, server);
|
||||
} else {
|
||||
ThreadHelper.execute(() -> {
|
||||
try {
|
||||
sendPayload(np, callback, server);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e("LanLink/sendPacket", "Async sendPayload failed for packet of type " + np.getType() + ". The Plugin was NOT notified.");
|
||||
Socket payloadSocket = null;
|
||||
OutputStream outputStream = null;
|
||||
InputStream inputStream;
|
||||
try {
|
||||
//Wait a maximum of 10 seconds for the other end to establish a connection with our socket, close it afterwards
|
||||
server.setSoTimeout(10*1000);
|
||||
|
||||
payloadSocket = server.accept();
|
||||
|
||||
//Convert to SSL if needed
|
||||
payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, false);
|
||||
|
||||
outputStream = payloadSocket.getOutputStream();
|
||||
inputStream = np.getPayload().getInputStream();
|
||||
|
||||
Log.i("KDE/LanLink", "Beginning to send payload");
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
long size = np.getPayloadSize();
|
||||
long progress = 0;
|
||||
long timeSinceLastUpdate = -1;
|
||||
while (!np.isCanceled() && (bytesRead = inputStream.read(buffer)) != -1) {
|
||||
//Log.e("ok",""+bytesRead);
|
||||
progress += bytesRead;
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
if (size > 0) {
|
||||
if (timeSinceLastUpdate + 500 < System.currentTimeMillis()) { //Report progress every half a second
|
||||
long percent = ((100 * progress) / size);
|
||||
callback.onProgressChanged((int) percent);
|
||||
timeSinceLastUpdate = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
outputStream.flush();
|
||||
Log.i("KDE/LanLink", "Finished sending payload ("+progress+" bytes written)");
|
||||
} catch(SSLHandshakeException e) {
|
||||
// The exception can be due to several causes. "Connection closed by peer" seems to be a common one.
|
||||
// If we could distinguish different cases we could react differently for some of them, but I haven't found how.
|
||||
Log.e("sendPacket","Payload SSLSocket failed");
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try { server.close(); } catch (Exception ignored) { }
|
||||
try { payloadSocket.close(); } catch (Exception ignored) { }
|
||||
np.getPayload().close();
|
||||
try { outputStream.close(); } catch (Exception ignored) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,59 +230,6 @@ public class LanLink extends BaseLink {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPayload(NetworkPacket np, Device.SendPacketStatusCallback callback, ServerSocket server) throws IOException {
|
||||
Socket payloadSocket = null;
|
||||
OutputStream outputStream = null;
|
||||
InputStream inputStream;
|
||||
try {
|
||||
if (!np.isCanceled()) {
|
||||
//Wait a maximum of 10 seconds for the other end to establish a connection with our socket, close it afterwards
|
||||
server.setSoTimeout(10 * 1000);
|
||||
|
||||
payloadSocket = server.accept();
|
||||
|
||||
//Convert to SSL if needed
|
||||
payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, false);
|
||||
|
||||
outputStream = payloadSocket.getOutputStream();
|
||||
inputStream = np.getPayload().getInputStream();
|
||||
|
||||
Log.i("KDE/LanLink", "Beginning to send payload for " + np.getType());
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
long size = np.getPayloadSize();
|
||||
long progress = 0;
|
||||
long timeSinceLastUpdate = -1;
|
||||
while (!np.isCanceled() && (bytesRead = inputStream.read(buffer)) != -1) {
|
||||
//Log.e("ok",""+bytesRead);
|
||||
progress += bytesRead;
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
if (size > 0) {
|
||||
if (timeSinceLastUpdate + 500 < System.currentTimeMillis()) { //Report progress every half a second
|
||||
long percent = ((100 * progress) / size);
|
||||
callback.onPayloadProgressChanged((int) percent);
|
||||
timeSinceLastUpdate = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
outputStream.flush();
|
||||
Log.i("KDE/LanLink", "Finished sending payload (" + progress + " bytes written)");
|
||||
}
|
||||
} catch(SocketTimeoutException e) {
|
||||
Log.e("LanLink", "Socket for payload in packet " + np.getType() + " timed out. The other end didn't fetch the payload.");
|
||||
} catch(SSLHandshakeException e) {
|
||||
// The exception can be due to several causes. "Connection closed by peer" seems to be a common one.
|
||||
// If we could distinguish different cases we could react differently for some of them, but I haven't found how.
|
||||
Log.e("sendPacket","Payload SSLSocket failed");
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try { server.close(); } catch (Exception ignored) { }
|
||||
try { payloadSocket.close(); } catch (Exception ignored) { }
|
||||
np.getPayload().close();
|
||||
try { outputStream.close(); } catch (Exception ignored) { }
|
||||
}
|
||||
}
|
||||
|
||||
private void receivedNetworkPacket(NetworkPacket np) {
|
||||
|
||||
if (np.hasPayloadTransferInfo()) {
|
||||
@@ -257,4 +250,14 @@ public class LanLink extends BaseLink {
|
||||
packetReceived(np);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
|
||||
return true; //FIXME: Current implementation is broken, so for now we will keep links always established
|
||||
|
||||
//We keep the remotely initiated connections, since the remotes require them if they want to request
|
||||
//pairing to us, or connections that are already paired.
|
||||
//return (connectionSource == ConnectionStarted.Remotely);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -9,16 +9,17 @@ package org.kde.kdeconnect.Backends.LanBackend;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
|
||||
|
||||
@@ -65,7 +66,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
private DatagramSocket udpServer;
|
||||
|
||||
private long lastBroadcast = 0;
|
||||
private final static long delayBetweenBroadcasts = 200;
|
||||
private final static long delayBetweenBroadcasts = 500;
|
||||
|
||||
private boolean listening = false;
|
||||
|
||||
@@ -195,13 +196,13 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
|
||||
if (isDeviceTrusted && !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) {
|
||||
return;
|
||||
}
|
||||
device.unpair();
|
||||
//Retry as unpaired
|
||||
identityPacketReceived(identityPacket, socket, connectionStarted);
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
Device device = service.getDevice(deviceId);
|
||||
if (device == null) return;
|
||||
device.unpair();
|
||||
//Retry as unpaired
|
||||
identityPacketReceived(identityPacket, socket, connectionStarted);
|
||||
});
|
||||
}
|
||||
|
||||
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + identityPacket.getString("deviceName") + " trusted:" + isDeviceTrusted);
|
||||
@@ -211,15 +212,16 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
String mode = clientMode ? "client" : "server";
|
||||
try {
|
||||
Certificate certificate = event.getPeerCertificates()[0];
|
||||
identityPacket.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
|
||||
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
|
||||
addLink(deviceId, certificate, identityPacket, sslsocket);
|
||||
addLink(identityPacket, sslsocket, connectionStarted);
|
||||
} catch (Exception e) {
|
||||
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"), e);
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
device.unpair();
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
Device device = service.getDevice(deviceId);
|
||||
if (device == null) return;
|
||||
device.unpair();
|
||||
});
|
||||
}
|
||||
});
|
||||
//Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection
|
||||
@@ -251,25 +253,26 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
* {@link Device#addLink(NetworkPacket, BaseLink)} crashes on some devices running Oreo 8.1 (SDK level 27).
|
||||
* </p>
|
||||
*
|
||||
* @param deviceId remote device id
|
||||
* @param certificate remote device certificate
|
||||
* @param identityPacket identity packet with the remote device's device name, type, protocol version, etc.
|
||||
* @param socket a new Socket, which should be used to send and receive packets from the remote device
|
||||
* @param identityPacket representation of remote device
|
||||
* @param socket a new Socket, which should be used to receive packets from the remote device
|
||||
* @param connectionOrigin which side started this connection
|
||||
* @throws IOException if an exception is thrown by {@link LanLink#reset(SSLSocket, LanLink.ConnectionStarted)}
|
||||
*/
|
||||
private void addLink(String deviceId, Certificate certificate, final NetworkPacket identityPacket, SSLSocket socket) throws IOException {
|
||||
private void addLink(final NetworkPacket identityPacket, SSLSocket socket, LanLink.ConnectionStarted connectionOrigin) throws IOException {
|
||||
|
||||
String deviceId = identityPacket.getString("deviceId");
|
||||
LanLink currentLink = visibleComputers.get(deviceId);
|
||||
if (currentLink != null) {
|
||||
//Update old link
|
||||
Log.i("KDE/LanLinkProvider", "Reusing same link for device " + deviceId);
|
||||
final Socket oldSocket = currentLink.reset(socket);
|
||||
final Socket oldSocket = currentLink.reset(socket, connectionOrigin);
|
||||
//Log.e("KDE/LanLinkProvider", "Replacing socket. old: "+ oldSocket.hashCode() + " - new: "+ socket.hashCode());
|
||||
} else {
|
||||
Log.i("KDE/LanLinkProvider", "Creating a new link for device " + deviceId);
|
||||
//Let's create the link
|
||||
LanLink link = new LanLink(context, deviceId, this, socket);
|
||||
LanLink link = new LanLink(context, deviceId, this, socket, connectionOrigin);
|
||||
visibleComputers.put(deviceId, link);
|
||||
connectionAccepted(deviceId, certificate, identityPacket, link);
|
||||
connectionAccepted(identityPacket, link);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +287,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
udpServer.setBroadcast(true);
|
||||
} catch (SocketException e) {
|
||||
Log.e("LanLinkProvider", "Error creating udp server", e);
|
||||
throw new RuntimeException(e);
|
||||
return;
|
||||
}
|
||||
ThreadHelper.execute(() -> {
|
||||
while (listening) {
|
||||
@@ -305,9 +308,9 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
private void setupTcpListener() {
|
||||
try {
|
||||
tcpServer = openServerSocketOnFreePort(MIN_PORT);
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
Log.e("LanLinkProvider", "Error creating tcp server", e);
|
||||
throw new RuntimeException(e);
|
||||
return;
|
||||
}
|
||||
ThreadHelper.execute(() -> {
|
||||
while (listening) {
|
||||
@@ -366,8 +369,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
|
||||
NetworkPacket identity = NetworkPacket.createIdentityPacket(context);
|
||||
if (tcpServer == null || !tcpServer.isBound()) {
|
||||
Log.i("LanLinkProvider", "Won't broadcast UDP packet if TCP socket is not ready yet");
|
||||
return;
|
||||
throw new RuntimeException("Wont't broadcast UDP packet if TCP socket is not ready");
|
||||
}
|
||||
int port = tcpServer.getLocalPort();
|
||||
identity.set("tcpPort", port);
|
||||
@@ -383,7 +385,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
}
|
||||
|
||||
if (bytes != null) {
|
||||
Log.i("KDE/LanLinkProvider","Sending broadcast to "+iplist.size()+" ips");
|
||||
//Log.e("KDE/LanLinkProvider","Sending packet to "+iplist.size()+" ips");
|
||||
for (String ipstr : iplist) {
|
||||
try {
|
||||
InetAddress client = InetAddress.getByName(ipstr);
|
||||
|
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@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.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BasePairingHandler;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class LanPairingHandler extends BasePairingHandler {
|
||||
|
||||
private Timer mPairingTimer;
|
||||
|
||||
public LanPairingHandler(Device device, final PairingHandlerCallback callback) {
|
||||
super(device, callback);
|
||||
|
||||
if (device.isPaired()) {
|
||||
mPairStatus = PairStatus.Paired;
|
||||
} else {
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
}
|
||||
}
|
||||
|
||||
private NetworkPacket createPairPacket() {
|
||||
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_PAIR);
|
||||
np.set("pair", true);
|
||||
return np;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(NetworkPacket np) {
|
||||
|
||||
boolean wantsPair = np.getBoolean("pair");
|
||||
|
||||
if (wantsPair == isPaired()) {
|
||||
if (mPairStatus == PairStatus.Requested) {
|
||||
//Log.e("Device","Unpairing (pair rejected)");
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
hidePairingNotification();
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (wantsPair) {
|
||||
|
||||
if (mPairStatus == PairStatus.Requested) { //We started pairing
|
||||
|
||||
hidePairingNotification();
|
||||
|
||||
pairingDone();
|
||||
|
||||
} else {
|
||||
|
||||
// If device is already paired, accept pairing silently
|
||||
if (mDevice.isPaired()) {
|
||||
acceptPairing();
|
||||
return;
|
||||
}
|
||||
|
||||
// Pairing notifications are still managed by device as there is no other way to
|
||||
// know about notificationId to cancel notification when PairActivity is started
|
||||
// Even putting notificationId in intent does not work because PairActivity can be
|
||||
// started from MainActivity too, so then notificationId cannot be set
|
||||
hidePairingNotification();
|
||||
mDevice.displayPairingNotification();
|
||||
|
||||
mPairingTimer = new Timer();
|
||||
|
||||
mPairingTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.w("KDE/Device","Unpairing (timeout B)");
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
hidePairingNotification();
|
||||
}
|
||||
}, 25*1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
|
||||
mPairStatus = PairStatus.RequestedByPeer;
|
||||
mCallback.incomingRequest();
|
||||
|
||||
}
|
||||
} else {
|
||||
Log.i("KDE/Pairing", "Unpair request");
|
||||
|
||||
if (mPairStatus == PairStatus.Requested) {
|
||||
hidePairingNotification();
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
|
||||
} else if (mPairStatus == PairStatus.Paired) {
|
||||
mCallback.unpaired();
|
||||
}
|
||||
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPairing() {
|
||||
|
||||
Device.SendPacketStatusCallback statusCallback = new Device.SendPacketStatusCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
hidePairingNotification(); //Will stop the pairingTimer if it was running
|
||||
mPairingTimer = new Timer();
|
||||
mPairingTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_timed_out));
|
||||
Log.w("KDE/Device","Unpairing (timeout A)");
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
}
|
||||
}, 30*1000); //Time to wait for the other to accept
|
||||
mPairStatus = PairStatus.Requested;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
Log.e("LanPairing/onFailure", "Exception", e);
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.runcommand_notreachable));
|
||||
}
|
||||
};
|
||||
mDevice.sendPacket(createPairPacket(), statusCallback);
|
||||
}
|
||||
|
||||
private void hidePairingNotification() {
|
||||
mDevice.hidePairingNotification();
|
||||
if (mPairingTimer != null) {
|
||||
mPairingTimer .cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptPairing() {
|
||||
hidePairingNotification();
|
||||
Device.SendPacketStatusCallback statusCallback = new Device.SendPacketStatusCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
pairingDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
Log.e("LanPairing/onFailure", "Exception", e);
|
||||
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_not_reachable));
|
||||
}
|
||||
};
|
||||
mDevice.sendPacket(createPairPacket(), statusCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectPairing() {
|
||||
hidePairingNotification();
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_PAIR);
|
||||
np.set("pair", false);
|
||||
mDevice.sendPacket(np);
|
||||
}
|
||||
|
||||
private void pairingDone() {
|
||||
// Store device information needed to create a Device object in a future
|
||||
//Log.e("KDE/PairingDone", "Pairing Done");
|
||||
SharedPreferences.Editor editor = mDevice.getContext().getSharedPreferences(mDevice.getDeviceId(), Context.MODE_PRIVATE).edit();
|
||||
|
||||
try {
|
||||
String encodedCertificate = Base64.encodeToString(mDevice.certificate.getEncoded(), 0);
|
||||
editor.putString("certificate", encodedCertificate);
|
||||
} catch (NullPointerException n) {
|
||||
Log.w("KDE/PairingDone", "Certificate is null, remote device does not support ssl", n);
|
||||
} catch (CertificateEncodingException c) {
|
||||
Log.e("KDE/PairingDOne", "Error encoding certificate", c);
|
||||
} catch (Exception e) {
|
||||
Log.e("KDE/Pairng", "Exception", e);
|
||||
}
|
||||
editor.apply();
|
||||
|
||||
mPairStatus = PairStatus.Paired;
|
||||
mCallback.pairingDone();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpair() {
|
||||
mPairStatus = PairStatus.NotPaired;
|
||||
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_PAIR);
|
||||
np.set("pair", false);
|
||||
mDevice.sendPacket(np);
|
||||
}
|
||||
}
|
@@ -8,11 +8,11 @@ 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.Backends.BasePairingHandler;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
@@ -27,14 +27,19 @@ public class LoopbackLink extends BaseLink {
|
||||
return "LoopbackLink";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) {
|
||||
return new LoopbackPairingHandler(device, callback);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@Override
|
||||
public boolean sendPacket(@NonNull NetworkPacket in, @NonNull Device.SendPacketStatusCallback callback, boolean sendPayloadFromSameThread) {
|
||||
public boolean sendPacket(NetworkPacket in, Device.SendPacketStatusCallback callback) {
|
||||
packetReceived(in);
|
||||
if (in.hasPayload()) {
|
||||
callback.onPayloadProgressChanged(0);
|
||||
callback.onProgressChanged(0);
|
||||
in.setPayload(in.getPayload());
|
||||
callback.onPayloadProgressChanged(100);
|
||||
callback.onProgressChanged(100);
|
||||
}
|
||||
callback.onSuccess();
|
||||
return true;
|
||||
|
@@ -9,8 +9,6 @@ package org.kde.kdeconnect.Backends.LoopbackBackend;
|
||||
import android.content.Context;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||
@@ -33,10 +31,14 @@ public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||
@Override
|
||||
public void onNetworkChange() {
|
||||
NetworkPacket np = NetworkPacket.createIdentityPacket(context);
|
||||
String deviceId = DeviceHelper.getDeviceId(context);
|
||||
connectionAccepted(deviceId, SslHelper.certificate, np, new LoopbackLink(context, this));
|
||||
connectionAccepted(np, new LoopbackLink(context, this));
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "LoopbackLinkProvider";
|
||||
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015 Vineet Garg <grg.vineet@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.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BasePairingHandler;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
public class LoopbackPairingHandler extends BasePairingHandler {
|
||||
|
||||
public LoopbackPairingHandler(Device device, PairingHandlerCallback callback) {
|
||||
super(device, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(NetworkPacket np) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPairing() {
|
||||
Log.i("LoopbackPairing", "requestPairing");
|
||||
mCallback.pairingDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptPairing() {
|
||||
Log.i("LoopbackPairing", "acceptPairing");
|
||||
mCallback.pairingDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rejectPairing() {
|
||||
Log.i("LoopbackPairing", "rejectPairing");
|
||||
mCallback.unpaired();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpair() {
|
||||
Log.i("LoopbackPairing", "unpair");
|
||||
mCallback.unpaired();
|
||||
}
|
||||
|
||||
}
|
@@ -14,28 +14,34 @@ import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
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.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.provider.Settings;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.LoopbackBackend.LoopbackLinkProvider;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.SendFileActivity;
|
||||
@@ -43,34 +49,92 @@ import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
//import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
public interface DeviceListChangedCallback {
|
||||
void onDeviceListChanged(boolean isConnectedToNonCellularNetwork);
|
||||
}
|
||||
|
||||
public interface PluginCallback<T extends Plugin> {
|
||||
void run(T plugin);
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<String, DeviceListChangedCallback> deviceListChangedCallbacks = new ConcurrentHashMap<>();
|
||||
|
||||
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<>();
|
||||
|
||||
private final ConcurrentHashMap<String, Device> devices = new ConcurrentHashMap<>();
|
||||
|
||||
private final HashSet<Object> discoveryModeAcquisitions = new HashSet<>();
|
||||
|
||||
public static BackgroundService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// 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;
|
||||
boolean isConnectedToNonCellularNetwork; // True when connected over wifi/usb/bluetooth/(anything other than cellular)
|
||||
|
||||
private boolean acquireDiscoveryMode(Object key) {
|
||||
boolean wasEmpty = discoveryModeAcquisitions.isEmpty();
|
||||
discoveryModeAcquisitions.add(key);
|
||||
if (wasEmpty) {
|
||||
onNetworkChange();
|
||||
}
|
||||
//Log.e("acquireDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
|
||||
return wasEmpty;
|
||||
}
|
||||
|
||||
public void updateForegroundNotification() {
|
||||
private void releaseDiscoveryMode(Object key) {
|
||||
boolean removed = discoveryModeAcquisitions.remove(key);
|
||||
//Log.e("releaseDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
|
||||
if (removed && discoveryModeAcquisitions.isEmpty()) {
|
||||
cleanDevices();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInDiscoveryMode() {
|
||||
//return !discoveryModeAcquisitions.isEmpty();
|
||||
return true; // Keep it always on for now
|
||||
}
|
||||
|
||||
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
||||
@Override
|
||||
public void incomingRequest() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingSuccessful() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingFailed(String error) {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpaired() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public void onDeviceListChanged() {
|
||||
for (DeviceListChangedCallback callback : deviceListChangedCallbacks.values()) {
|
||||
callback.onDeviceListChanged(isConnectedToNonCellularNetwork);
|
||||
}
|
||||
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
//Update the foreground notification with the currently connected device list
|
||||
NotificationManager nm = ContextCompat.getSystemService(this, NotificationManager.class);
|
||||
@@ -78,17 +142,97 @@ public class BackgroundService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRememberedDevicesFromSettings() {
|
||||
//Log.e("BackgroundService", "Loading remembered trusted devices");
|
||||
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
Set<String> trustedDevices = preferences.getAll().keySet();
|
||||
for (String deviceId : trustedDevices) {
|
||||
//Log.e("BackgroundService", "Loading device "+deviceId);
|
||||
if (preferences.getBoolean(deviceId, false)) {
|
||||
Device device = new Device(this, deviceId);
|
||||
devices.put(deviceId, device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerLinkProviders() {
|
||||
linkProviders.add(new LanLinkProvider(this));
|
||||
String testLabSetting = Settings.System.getString(getContentResolver(), "firebase.test.lab");
|
||||
if ("true".equals(testLabSetting)) {
|
||||
linkProviders.add(new LoopbackLinkProvider(this));
|
||||
}
|
||||
// linkProviders.add(new LoopbackLinkProvider(this));
|
||||
// linkProviders.add(new BluetoothLinkProvider(this));
|
||||
}
|
||||
|
||||
public ArrayList<BaseLinkProvider> getLinkProviders() {
|
||||
return linkProviders;
|
||||
}
|
||||
|
||||
public Device getDevice(String id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
private void cleanDevices() {
|
||||
ThreadHelper.execute(() -> {
|
||||
for (Device d : devices.values()) {
|
||||
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
|
||||
d.disconnect();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||
@Override
|
||||
public void onConnectionReceived(final NetworkPacket identityPacket, final BaseLink link) {
|
||||
|
||||
String deviceId = identityPacket.getString("deviceId");
|
||||
|
||||
Device device = devices.get(deviceId);
|
||||
|
||||
if (device != null) {
|
||||
Log.i("KDE/BackgroundService", "addLink, known device: " + deviceId);
|
||||
device.addLink(identityPacket, link);
|
||||
} else {
|
||||
Log.i("KDE/BackgroundService", "addLink,unknown device: " + deviceId);
|
||||
device = new Device(BackgroundService.this, identityPacket, link);
|
||||
if (device.isPaired() || device.isPairRequested() || device.isPairRequestedByPeer()
|
||||
|| link.linkShouldBeKeptAlive()
|
||||
|| isInDiscoveryMode()) {
|
||||
devices.put(deviceId, device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
} else {
|
||||
device.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(BaseLink link) {
|
||||
Device d = devices.get(link.getDeviceId());
|
||||
Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
||||
if (d != null) {
|
||||
d.removeLink(link);
|
||||
if (!d.isReachable() && !d.isPaired()) {
|
||||
//Log.e("onConnectionLost","Removing connection device because it was not paired");
|
||||
devices.remove(link.getDeviceId());
|
||||
d.removePairingCallback(devicePairingCallback);
|
||||
}
|
||||
} else {
|
||||
//Log.d("KDE/onConnectionLost","Removing connection to unknown device");
|
||||
}
|
||||
onDeviceListChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public ConcurrentHashMap<String, Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public void onNetworkChange() {
|
||||
Log.d("KDE/BackgroundService", "onNetworkChange");
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onNetworkChange();
|
||||
}
|
||||
@@ -106,14 +250,22 @@ public class BackgroundService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
public void addDeviceListChangedCallback(String key, DeviceListChangedCallback callback) {
|
||||
deviceListChangedCallbacks.put(key, callback);
|
||||
}
|
||||
|
||||
public void removeDeviceListChangedCallback(String key) {
|
||||
deviceListChangedCallbacks.remove(key);
|
||||
}
|
||||
|
||||
//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);
|
||||
DeviceHelper.initializeDeviceId(this);
|
||||
|
||||
// Register screen on listener
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||
@@ -129,19 +281,29 @@ public class BackgroundService extends Service {
|
||||
cm.registerNetworkCallback(networkRequestBuilder.build(), new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
connectedToNonCellularNetwork.postValue(true);
|
||||
isConnectedToNonCellularNetwork = true;
|
||||
onDeviceListChanged();
|
||||
onNetworkChange();
|
||||
}
|
||||
@Override
|
||||
public void onLost(Network network) {
|
||||
connectedToNonCellularNetwork.postValue(false);
|
||||
isConnectedToNonCellularNetwork = false;
|
||||
onDeviceListChanged();
|
||||
}
|
||||
});
|
||||
|
||||
applicationInstance = KdeConnect.getInstance();
|
||||
Log.i("KDE/BackgroundService", "Service not started yet, initializing...");
|
||||
|
||||
PluginFactory.initPluginInfo(getBaseContext());
|
||||
initializeSecurityParameters();
|
||||
NotificationHelper.initializeChannels(this);
|
||||
loadRememberedDevicesFromSettings();
|
||||
migratePluginSettings();
|
||||
registerLinkProviders();
|
||||
addConnectionListener(applicationInstance.getConnectionListener()); // Link Providers need to be already registered
|
||||
|
||||
//Link Providers need to be already registered
|
||||
addConnectionListener(deviceListener);
|
||||
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStart();
|
||||
}
|
||||
@@ -163,11 +325,36 @@ public class BackgroundService extends Service {
|
||||
return networkRequestBuilder;
|
||||
}
|
||||
|
||||
private void migratePluginSettings() {
|
||||
SharedPreferences globalPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
for (String pluginKey : PluginFactory.getAvailablePlugins()) {
|
||||
if (PluginFactory.getPluginInfo(pluginKey).supportsDeviceSpecificSettings()) {
|
||||
Iterator<Device> it = devices.values().iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
Device device = it.next();
|
||||
Plugin plugin = PluginFactory.instantiatePluginForDevice(getBaseContext(), pluginKey, device);
|
||||
|
||||
if (plugin == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
plugin.copyGlobalToDeviceSpecificSettings(globalPrefs);
|
||||
if (!it.hasNext()) {
|
||||
plugin.removeSettings(globalPrefs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void changePersistentNotificationVisibility(boolean visible) {
|
||||
NotificationManager nm = ContextCompat.getSystemService(this, NotificationManager.class);
|
||||
if (visible) {
|
||||
updateForegroundNotification();
|
||||
nm.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
} else {
|
||||
Stop();
|
||||
stopForeground(true);
|
||||
Start(this);
|
||||
}
|
||||
}
|
||||
@@ -178,7 +365,7 @@ public class BackgroundService extends Service {
|
||||
|
||||
ArrayList<String> connectedDevices = new ArrayList<>();
|
||||
ArrayList<String> connectedDeviceIds = new ArrayList<>();
|
||||
for (Device device : applicationInstance.getDevices().values()) {
|
||||
for (Device device : getDevices().values()) {
|
||||
if (device.isReachable() && device.isPaired()) {
|
||||
connectedDeviceIds.add(device.getDeviceId());
|
||||
connectedDevices.add(device.getName());
|
||||
@@ -222,7 +409,7 @@ public class BackgroundService extends Service {
|
||||
|
||||
if (connectedDeviceIds.size() == 1) {
|
||||
String deviceId = connectedDeviceIds.get(0);
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
Device device = getDevice(deviceId);
|
||||
if (device != null) {
|
||||
// Adding two action buttons only when there is a single device connected.
|
||||
// Setting up Send File Intent.
|
||||
@@ -245,24 +432,49 @@ public class BackgroundService extends Service {
|
||||
return notification.build();
|
||||
}
|
||||
|
||||
private void initializeSecurityParameters() {
|
||||
RsaHelper.initialiseRsaKeys(this);
|
||||
SslHelper.initialiseCertificate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d("KdeConnect/BgService", "onDestroy");
|
||||
stopForeground(true);
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStop();
|
||||
}
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("BackgroundService");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
return new Binder();
|
||||
}
|
||||
|
||||
|
||||
//To use the service from the gui
|
||||
|
||||
public interface InstanceCallback {
|
||||
void onServiceStart(BackgroundService service);
|
||||
}
|
||||
|
||||
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<>();
|
||||
|
||||
private final static Lock mutex = new ReentrantLock(true);
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d("KDE/BackgroundService", "onStartCommand");
|
||||
//This will be called for each intent launch, even if the service is already started and it is reused
|
||||
mutex.lock();
|
||||
try {
|
||||
for (InstanceCallback c : callbacks) {
|
||||
c.onServiceStart(this);
|
||||
}
|
||||
callbacks.clear();
|
||||
} finally {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
|
||||
@@ -270,27 +482,43 @@ public class BackgroundService extends Service {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
}
|
||||
}
|
||||
if (intent != null && intent.getBooleanExtra("refresh", false)) {
|
||||
onNetworkChange();
|
||||
}
|
||||
return Service.START_STICKY;
|
||||
}
|
||||
|
||||
public static void Start(Context context) {
|
||||
Log.d("KDE/BackgroundService", "Start");
|
||||
Intent intent = new Intent(context, BackgroundService.class);
|
||||
ContextCompat.startForegroundService(context, intent);
|
||||
private static void Start(Context c) {
|
||||
RunCommand(c, null);
|
||||
}
|
||||
|
||||
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 static void RunCommand(final Context c, final InstanceCallback callback) {
|
||||
ThreadHelper.execute(() -> {
|
||||
if (callback != null) {
|
||||
mutex.lock();
|
||||
try {
|
||||
callbacks.add(callback);
|
||||
} finally {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
ContextCompat.startForegroundService(c, new Intent(c, BackgroundService.class));
|
||||
});
|
||||
}
|
||||
|
||||
public void Stop() {
|
||||
stopForeground(true);
|
||||
}
|
||||
public static <T extends Plugin> void RunWithPlugin(final Context c, final String deviceId, final Class<T> pluginClass, final PluginCallback<T> cb) {
|
||||
RunCommand(c, service -> {
|
||||
Device device = service.getDevice(deviceId);
|
||||
|
||||
if (device == null) {
|
||||
Log.e("BackgroundService", "Device " + deviceId + " not found");
|
||||
return;
|
||||
}
|
||||
|
||||
final T plugin = device.getPlugin(pluginClass);
|
||||
|
||||
if (plugin == null) {
|
||||
Log.e("BackgroundService", "Device " + device.getName() + " does not have plugin " + pluginClass.getName());
|
||||
return;
|
||||
}
|
||||
cb.run(plugin);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -28,23 +29,25 @@ import org.apache.commons.collections4.MultiValuedMap;
|
||||
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BasePairingHandler;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect.UserInterface.PairingHandler;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -59,17 +62,24 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
public Certificate certificate;
|
||||
private int notificationId;
|
||||
private int protocolVersion;
|
||||
|
||||
private DeviceType deviceType;
|
||||
PairingHandler pairingHandler;
|
||||
private final CopyOnWriteArrayList<PairingHandler.PairingCallback> pairingCallbacks = new CopyOnWriteArrayList<>();
|
||||
private PairStatus pairStatus;
|
||||
|
||||
private final CopyOnWriteArrayList<PairingCallback> pairingCallback = new CopyOnWriteArrayList<>();
|
||||
private final Map<String, BasePairingHandler> pairingHandlers = new HashMap<>();
|
||||
|
||||
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
|
||||
private DevicePacketQueue packetQueue;
|
||||
|
||||
private List<String> supportedPlugins = new ArrayList<>();
|
||||
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Plugin> pluginsWithoutPermissions = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Plugin> pluginsWithoutOptionalPermissions = new ConcurrentHashMap<>();
|
||||
private MultiValuedMap<String, String> pluginsByIncomingInterface = new ArrayListValuedHashMap<>();
|
||||
|
||||
private final SharedPreferences settings;
|
||||
|
||||
private final CopyOnWriteArrayList<PluginsChangedListener> pluginsChangedListeners = new CopyOnWriteArrayList<>();
|
||||
private Set<String> incomingCapabilities = new HashSet<>();
|
||||
|
||||
@@ -82,7 +92,12 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
|
||||
public interface PluginsChangedListener {
|
||||
void onPluginsChanged(@NonNull Device device);
|
||||
void onPluginsChanged(Device device);
|
||||
}
|
||||
|
||||
public enum PairStatus {
|
||||
NotPaired,
|
||||
Paired
|
||||
}
|
||||
|
||||
public enum DeviceType {
|
||||
@@ -131,21 +146,28 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
// Remembered trusted device, we need to wait for a incoming Link to communicate
|
||||
Device(@NonNull Context context, @NonNull String deviceId) throws CertificateException {
|
||||
public interface PairingCallback {
|
||||
void incomingRequest();
|
||||
|
||||
void pairingSuccessful();
|
||||
|
||||
void pairingFailed(String error);
|
||||
|
||||
void unpaired();
|
||||
}
|
||||
|
||||
//Remembered trusted device, we need to wait for a incoming devicelink to communicate
|
||||
Device(Context context, String deviceId) {
|
||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
|
||||
//Log.e("Device","Constructor A");
|
||||
|
||||
this.context = context;
|
||||
|
||||
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.Paired);
|
||||
|
||||
this.deviceId = deviceId;
|
||||
this.name = settings.getString("deviceName", context.getString(R.string.unknown_device));
|
||||
this.protocolVersion = 0; //We don't know it yet
|
||||
this.pairStatus = PairStatus.Paired;
|
||||
this.protocolVersion = DeviceHelper.ProtocolVersion; //We don't know it yet
|
||||
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
|
||||
this.certificate = SslHelper.getDeviceCertificate(context, deviceId);
|
||||
|
||||
Log.i("Device","Loading trusted device: " + this.name);
|
||||
|
||||
//Assume every plugin is supported until addLink is called and we can get the actual list
|
||||
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
|
||||
@@ -154,24 +176,21 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
//reloadPluginsFromSettings();
|
||||
}
|
||||
|
||||
// Device known via an incoming connection sent to us via a Link, we don't trust it yet
|
||||
Device(@NonNull Context context, @NonNull String deviceId, @NonNull Certificate certificate, @NonNull NetworkPacket identityPacket, @NonNull BaseLink dl) {
|
||||
Log.i("Device","Creating untrusted device");
|
||||
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
||||
Device(Context context, NetworkPacket np, BaseLink dl) {
|
||||
|
||||
//Log.e("Device","Constructor B");
|
||||
|
||||
this.context = context;
|
||||
|
||||
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.NotPaired);
|
||||
|
||||
this.deviceId = deviceId;
|
||||
this.certificate = certificate;
|
||||
|
||||
// The following properties are read from the identityPacket in addLink since they can change in future identity packets
|
||||
this.name = context.getString(R.string.unknown_device);
|
||||
this.deviceType = DeviceType.Computer;
|
||||
this.deviceId = np.getString("deviceId");
|
||||
this.name = context.getString(R.string.unknown_device); //We read it in addLink
|
||||
this.pairStatus = PairStatus.NotPaired;
|
||||
this.protocolVersion = 0;
|
||||
this.deviceType = DeviceType.Computer;
|
||||
|
||||
addLink(identityPacket, dl);
|
||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
|
||||
addLink(np, dl);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -205,108 +224,149 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
//
|
||||
|
||||
public boolean isPaired() {
|
||||
return pairingHandler.getState() == PairingHandler.PairState.Paired;
|
||||
return pairStatus == PairStatus.Paired;
|
||||
}
|
||||
|
||||
/* Asks all pairing handlers that, is pair requested? */
|
||||
public boolean isPairRequested() {
|
||||
return pairingHandler.getState() == PairingHandler.PairState.Requested;
|
||||
boolean pairRequested = false;
|
||||
for (BasePairingHandler ph : pairingHandlers.values()) {
|
||||
pairRequested = pairRequested || ph.isPairRequested();
|
||||
}
|
||||
return pairRequested;
|
||||
}
|
||||
|
||||
/* Asks all pairing handlers that, is pair requested by peer? */
|
||||
public boolean isPairRequestedByPeer() {
|
||||
return pairingHandler.getState() == PairingHandler.PairState.RequestedByPeer;
|
||||
boolean pairRequestedByPeer = false;
|
||||
for (BasePairingHandler ph : pairingHandlers.values()) {
|
||||
pairRequestedByPeer = pairRequestedByPeer || ph.isPairRequestedByPeer();
|
||||
}
|
||||
return pairRequestedByPeer;
|
||||
}
|
||||
|
||||
public void addPairingCallback(PairingHandler.PairingCallback callback) {
|
||||
pairingCallbacks.add(callback);
|
||||
public void addPairingCallback(PairingCallback callback) {
|
||||
pairingCallback.add(callback);
|
||||
}
|
||||
|
||||
public void removePairingCallback(PairingHandler.PairingCallback callback) {
|
||||
pairingCallbacks.remove(callback);
|
||||
public void removePairingCallback(PairingCallback callback) {
|
||||
pairingCallback.remove(callback);
|
||||
}
|
||||
|
||||
public void requestPairing() {
|
||||
pairingHandler.requestPairing();
|
||||
|
||||
Resources res = context.getResources();
|
||||
|
||||
if (isPaired()) {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(res.getString(R.string.error_already_paired));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isReachable()) {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(res.getString(R.string.error_not_reachable));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (BasePairingHandler ph : pairingHandlers.values()) {
|
||||
ph.requestPairing();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void unpair() {
|
||||
pairingHandler.unpair();
|
||||
|
||||
for (BasePairingHandler ph : pairingHandlers.values()) {
|
||||
ph.unpair();
|
||||
}
|
||||
unpairInternal(); // Even if there are no pairing handlers, unpair
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does not send an unpair packet, instead it unpairs internally by deleting trusted device info.
|
||||
* Likely to be called after sending packet from pairing handler
|
||||
*/
|
||||
private void unpairInternal() {
|
||||
|
||||
//Log.e("Device","Unpairing (unpairInternal)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
preferences.edit().remove(deviceId).apply();
|
||||
|
||||
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
devicePreferences.edit().clear().apply();
|
||||
|
||||
for (PairingCallback cb : pairingCallback) cb.unpaired();
|
||||
|
||||
reloadPluginsFromSettings();
|
||||
|
||||
}
|
||||
|
||||
/* This method should be called after pairing is done from pairing handler. Calling this method again should not create any problem as most of the things will get over writter*/
|
||||
private void pairingDone() {
|
||||
|
||||
//Log.e("Device", "Storing as trusted, deviceId: "+deviceId);
|
||||
|
||||
hidePairingNotification();
|
||||
|
||||
pairStatus = PairStatus.Paired;
|
||||
|
||||
//Store as trusted device
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
preferences.edit().putBoolean(deviceId, true).apply();
|
||||
|
||||
SharedPreferences.Editor editor = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE).edit();
|
||||
editor.putString("deviceName", name);
|
||||
editor.putString("deviceType", deviceType.toString());
|
||||
editor.apply();
|
||||
|
||||
reloadPluginsFromSettings();
|
||||
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingSuccessful();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This method is called after accepting pair request form GUI */
|
||||
public void acceptPairing() {
|
||||
|
||||
Log.i("KDE/Device", "Accepted pair request started by the other device");
|
||||
pairingHandler.acceptPairing();
|
||||
|
||||
for (BasePairingHandler ph : pairingHandlers.values()) {
|
||||
ph.acceptPairing();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This method is called after rejecting pairing from GUI */
|
||||
public void cancelPairing() {
|
||||
Log.i("KDE/Device", "This side cancelled the pair request");
|
||||
pairingHandler.cancelPairing();
|
||||
public void rejectPairing() {
|
||||
|
||||
Log.i("KDE/Device", "Rejected pair request started by the other device");
|
||||
|
||||
//Log.e("Device","Unpairing (rejectPairing)");
|
||||
pairStatus = PairStatus.NotPaired;
|
||||
|
||||
for (BasePairingHandler ph : pairingHandlers.values()) {
|
||||
ph.rejectPairing();
|
||||
}
|
||||
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(context.getString(R.string.error_canceled_by_user));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PairingHandler.PairingCallback pairingCallback = new PairingHandler.PairingCallback() {
|
||||
@Override
|
||||
public void incomingPairRequest() {
|
||||
displayPairingNotification();
|
||||
for (PairingHandler.PairingCallback cb : pairingCallbacks) {
|
||||
cb.incomingPairRequest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingSuccessful() {
|
||||
hidePairingNotification();
|
||||
|
||||
// Store current device certificate so we can check it in the future (TOFU)
|
||||
SharedPreferences.Editor editor = context.getSharedPreferences(getDeviceId(), Context.MODE_PRIVATE).edit();
|
||||
try {
|
||||
String encodedCertificate = Base64.encodeToString(certificate.getEncoded(), 0);
|
||||
editor.putString("certificate", encodedCertificate);
|
||||
} catch(CertificateEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
editor.putString("deviceName", name);
|
||||
editor.putString("deviceType", deviceType.toString());
|
||||
editor.apply();
|
||||
|
||||
// Store as trusted device
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
preferences.edit().putBoolean(deviceId, true).apply();
|
||||
|
||||
reloadPluginsFromSettings();
|
||||
|
||||
for (PairingHandler.PairingCallback cb : pairingCallbacks) {
|
||||
cb.pairingSuccessful();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingFailed(String error) {
|
||||
hidePairingNotification();
|
||||
for (PairingHandler.PairingCallback cb : pairingCallbacks) {
|
||||
cb.pairingFailed(error);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpaired() {
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
preferences.edit().remove(deviceId).apply();
|
||||
|
||||
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
devicePreferences.edit().clear().apply();
|
||||
|
||||
for (PairingHandler.PairingCallback cb : pairingCallbacks) {
|
||||
cb.unpaired();
|
||||
}
|
||||
|
||||
reloadPluginsFromSettings();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Notification related methods used during pairing
|
||||
//
|
||||
public int getNotificationId() {
|
||||
return notificationId;
|
||||
}
|
||||
|
||||
public void displayPairingNotification() {
|
||||
|
||||
@@ -323,9 +383,11 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
Intent rejectIntent = new Intent(getContext(), MainActivity.class);
|
||||
|
||||
acceptIntent.putExtra(MainActivity.EXTRA_DEVICE_ID, getDeviceId());
|
||||
//acceptIntent.putExtra("notificationId", notificationId);
|
||||
acceptIntent.putExtra(MainActivity.PAIR_REQUEST_STATUS, MainActivity.PAIRING_ACCEPTED);
|
||||
|
||||
rejectIntent.putExtra(MainActivity.EXTRA_DEVICE_ID, getDeviceId());
|
||||
//rejectIntent.putExtra("notificationId", notificationId);
|
||||
rejectIntent.putExtra(MainActivity.PAIR_REQUEST_STATUS, MainActivity.PAIRING_REJECTED);
|
||||
|
||||
PendingIntent acceptedPendingIntent = PendingIntent.getActivity(getContext(), 2, acceptIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
|
||||
@@ -342,7 +404,6 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
.setContentText(res.getString(R.string.pairing_verification_code, verificationKeyShort))
|
||||
.setTicker(res.getString(R.string.pair_requested))
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentIntent(pendingIntent)
|
||||
.addAction(R.drawable.ic_accept_pairing_24dp, res.getString(R.string.pairing_accept), acceptedPendingIntent)
|
||||
.addAction(R.drawable.ic_reject_pairing_24dp, res.getString(R.string.pairing_reject), rejectedPendingIntent)
|
||||
.setAutoCancel(true)
|
||||
@@ -359,7 +420,7 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
|
||||
//
|
||||
// Link-related functions
|
||||
// ComputerLink-related functions
|
||||
//
|
||||
|
||||
public boolean isReachable() {
|
||||
@@ -387,11 +448,63 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
this.deviceType = DeviceType.FromString(identityPacket.getString("deviceType", "desktop"));
|
||||
}
|
||||
|
||||
if (identityPacket.has("certificate")) {
|
||||
String certificateString = identityPacket.getString("certificate");
|
||||
|
||||
try {
|
||||
byte[] certificateBytes = Base64.decode(certificateString, 0);
|
||||
certificate = SslHelper.parseCertificate(certificateBytes);
|
||||
Log.i("KDE/Device", "Got certificate ");
|
||||
} catch (Exception e) {
|
||||
Log.e("KDE/Device", "Error getting certificate", e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
|
||||
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
||||
link.setPrivateKey(privateKey);
|
||||
} catch (Exception e) {
|
||||
Log.e("KDE/Device", "Exception reading our own private key", e); //Should not happen
|
||||
}
|
||||
|
||||
Log.i("KDE/Device", "addLink " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size());
|
||||
|
||||
if (!pairingHandlers.containsKey(link.getName())) {
|
||||
BasePairingHandler.PairingHandlerCallback callback = new BasePairingHandler.PairingHandlerCallback() {
|
||||
@Override
|
||||
public void incomingRequest() {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.incomingRequest();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingDone() {
|
||||
Device.this.pairingDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingFailed(String error) {
|
||||
for (PairingCallback cb : pairingCallback) {
|
||||
cb.pairingFailed(error);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpaired() {
|
||||
unpairInternal();
|
||||
}
|
||||
};
|
||||
pairingHandlers.put(link.getName(), link.getPairingHandler(this, callback));
|
||||
}
|
||||
|
||||
Set<String> outgoingCapabilities = identityPacket.getStringSet("outgoingCapabilities", null);
|
||||
Set<String> incomingCapabilities = identityPacket.getStringSet("incomingCapabilities", null);
|
||||
|
||||
|
||||
if (incomingCapabilities != null && outgoingCapabilities != null) {
|
||||
supportedPlugins = new Vector<>(PluginFactory.pluginsForCapabilities(incomingCapabilities, outgoingCapabilities));
|
||||
} else {
|
||||
@@ -406,6 +519,18 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
public void removeLink(BaseLink link) {
|
||||
//FilesHelper.LogOpenFileCount();
|
||||
|
||||
/* Remove pairing handler corresponding to that link too if it was the only link*/
|
||||
boolean linkPresent = false;
|
||||
for (BaseLink bl : links) {
|
||||
if (bl.getName().equals(link.getName())) {
|
||||
linkPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!linkPresent) {
|
||||
pairingHandlers.remove(link.getName());
|
||||
}
|
||||
|
||||
link.removePacketReceiver(this);
|
||||
links.remove(link);
|
||||
Log.i("KDE/Device", "removeLink: " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size());
|
||||
@@ -419,13 +544,19 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceived(@NonNull NetworkPacket np) {
|
||||
|
||||
DeviceStats.countReceived(getDeviceId(), np.getType());
|
||||
public void onPacketReceived(NetworkPacket np) {
|
||||
|
||||
if (NetworkPacket.PACKET_TYPE_PAIR.equals(np.getType())) {
|
||||
|
||||
Log.i("KDE/Device", "Pair packet");
|
||||
pairingHandler.packetReceived(np);
|
||||
|
||||
for (BasePairingHandler ph : pairingHandlers.values()) {
|
||||
try {
|
||||
ph.packetReceived(np);
|
||||
} catch (Exception e) {
|
||||
Log.e("PairingPacketReceived", "Exception", e);
|
||||
}
|
||||
}
|
||||
} else if (isPaired()) {
|
||||
// pluginsByIncomingInterface may not be built yet
|
||||
if(pluginsByIncomingInterface.isEmpty()) {
|
||||
@@ -478,7 +609,7 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
|
||||
public abstract void onFailure(Throwable e);
|
||||
|
||||
public void onPayloadProgressChanged(int percent) {
|
||||
public void onProgressChanged(int percent) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,22 +625,22 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
};
|
||||
|
||||
@AnyThread
|
||||
public void sendPacket(@NonNull NetworkPacket np) {
|
||||
public void sendPacket(NetworkPacket np) {
|
||||
sendPacket(np, -1, defaultCallback);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
public void sendPacket(@NonNull NetworkPacket np, int replaceID) {
|
||||
public void sendPacket(NetworkPacket np, int replaceID) {
|
||||
sendPacket(np, replaceID, defaultCallback);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public boolean sendPacketBlocking(@NonNull NetworkPacket np) {
|
||||
public boolean sendPacketBlocking(NetworkPacket np) {
|
||||
return sendPacketBlocking(np, defaultCallback);
|
||||
}
|
||||
|
||||
@AnyThread
|
||||
public void sendPacket(@NonNull final NetworkPacket np, @NonNull final SendPacketStatusCallback callback) {
|
||||
public void sendPacket(final NetworkPacket np, final SendPacketStatusCallback callback) {
|
||||
sendPacket(np, -1, callback);
|
||||
}
|
||||
|
||||
@@ -520,7 +651,7 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
* @param callback A callback for success/failure
|
||||
*/
|
||||
@AnyThread
|
||||
public void sendPacket(@NonNull final NetworkPacket np, int replaceID, @NonNull final SendPacketStatusCallback callback) {
|
||||
public void sendPacket(final NetworkPacket np, int replaceID, final SendPacketStatusCallback callback) {
|
||||
if (packetQueue == null) {
|
||||
callback.onFailure(new Exception("Device disconnected!"));
|
||||
} else {
|
||||
@@ -542,24 +673,16 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public boolean sendPacketBlocking(@NonNull final NetworkPacket np, @NonNull final SendPacketStatusCallback callback) {
|
||||
return sendPacketBlocking(np, callback, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send {@code np} over one of this device's connected {@link #links}.
|
||||
*
|
||||
* @param np the packet to send
|
||||
* @param callback a callback that can receive realtime updates
|
||||
* @param sendPayloadFromSameThread when set to true and np contains a Payload, this function
|
||||
* won't return until the Payload has been received by the
|
||||
* other end, or times out after 10 seconds
|
||||
* @param np the packet to send
|
||||
* @param callback a callback that can receive realtime updates
|
||||
* @return true if the packet was sent ok, false otherwise
|
||||
* @see BaseLink#sendPacket(NetworkPacket, SendPacketStatusCallback, boolean)
|
||||
* @see BaseLink#sendPacket(NetworkPacket, SendPacketStatusCallback)
|
||||
*/
|
||||
@WorkerThread
|
||||
public boolean sendPacketBlocking(@NonNull final NetworkPacket np, @NonNull final SendPacketStatusCallback callback, boolean sendPayloadFromSameThread) {
|
||||
public boolean sendPacketBlocking(final NetworkPacket np, final SendPacketStatusCallback callback) {
|
||||
|
||||
/*
|
||||
if (!m_outgoingCapabilities.contains(np.getType()) && !NetworkPacket.protocolPacketTypes.contains(np.getType())) {
|
||||
@@ -569,15 +692,12 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
*/
|
||||
|
||||
boolean success = false;
|
||||
//Make a copy to avoid concurrent modification exception if the original list changes
|
||||
for (final BaseLink link : links) {
|
||||
if (link == null) continue;
|
||||
try {
|
||||
success = link.sendPacket(np, callback, sendPayloadFromSameThread);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
DeviceStats.countSent(getDeviceId(), np.getType(), success);
|
||||
if (success) break;
|
||||
if (link == null)
|
||||
continue; //Since we made a copy, maybe somebody destroyed the link in the meanwhile
|
||||
success = link.sendPacket(np, callback);
|
||||
if (success) break; //If the link didn't call sendSuccess(), try the next one
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
@@ -760,6 +880,22 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deviceShouldBeKeptAlive() {
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
if (preferences.contains(getDeviceId())) {
|
||||
//Log.e("DeviceShouldBeKeptAlive", "because it's a paired device");
|
||||
return true; //Already paired
|
||||
}
|
||||
|
||||
for (BaseLink l : links) {
|
||||
if (l.linkShouldBeKeptAlive()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<String> getSupportedPlugins() {
|
||||
return supportedPlugins;
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
@@ -103,10 +101,7 @@ class DevicePacketQueue {
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
if (exit) {
|
||||
Log.i("DevicePacketQueue", "Terminating sending loop");
|
||||
break;
|
||||
}
|
||||
if (exit) break;
|
||||
|
||||
item = items.removeFirst();
|
||||
}
|
||||
@@ -120,5 +115,4 @@ class DevicePacketQueue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,192 +0,0 @@
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DeviceStats {
|
||||
|
||||
/**
|
||||
* Keep 24 hours of events
|
||||
*/
|
||||
private static final long EVENT_KEEP_WINDOW_MILLIS = 24 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Delete old (>24 hours, see EVENT_KEEP_WINDOW_MILLIS) events every 6 hours
|
||||
*/
|
||||
private static final long CLEANUP_INTERVAL_MILLIS = EVENT_KEEP_WINDOW_MILLIS/4;
|
||||
|
||||
private final static HashMap<String, PacketStats> eventsByDevice = new HashMap<>();
|
||||
private static long nextCleanup = System.currentTimeMillis() + CLEANUP_INTERVAL_MILLIS;
|
||||
|
||||
static class PacketStats {
|
||||
public long createdAtMillis = System.currentTimeMillis();
|
||||
public HashMap<String, ArrayList<Long>> receivedByType = new HashMap<>();
|
||||
public HashMap<String, ArrayList<Long>> sentSuccessfulByType = new HashMap<>();
|
||||
public HashMap<String, ArrayList<Long>> sentFailedByType = new HashMap<>();
|
||||
|
||||
static class Summary {
|
||||
final @NonNull String packetType;
|
||||
int received = 0;
|
||||
int sentSuccessful = 0;
|
||||
int sentFailed = 0;
|
||||
int total = 0;
|
||||
|
||||
Summary(@NonNull String packetType) {
|
||||
this.packetType = packetType;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public @NonNull Collection<Summary> getSummaries() {
|
||||
HashMap<String, Summary> countsByType = new HashMap<>();
|
||||
for (Map.Entry<String, ArrayList<Long>> entry : receivedByType.entrySet()) {
|
||||
Summary summary = countsByType.computeIfAbsent(entry.getKey(), Summary::new);
|
||||
summary.received += entry.getValue().size();
|
||||
summary.total += entry.getValue().size();
|
||||
}
|
||||
for (Map.Entry<String, ArrayList<Long>> entry : sentSuccessfulByType.entrySet()) {
|
||||
Summary summary = countsByType.computeIfAbsent(entry.getKey(), Summary::new);
|
||||
summary.sentSuccessful += entry.getValue().size();
|
||||
summary.total += entry.getValue().size();
|
||||
}
|
||||
for (Map.Entry<String, ArrayList<Long>> entry : sentFailedByType.entrySet()) {
|
||||
Summary summary = countsByType.computeIfAbsent(entry.getKey(), Summary::new);
|
||||
summary.sentFailed += entry.getValue().size();
|
||||
summary.total += entry.getValue().size();
|
||||
}
|
||||
return countsByType.values();
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static @NonNull String getStatsForDevice(@NonNull String deviceId) {
|
||||
|
||||
cleanupIfNeeded();
|
||||
|
||||
PacketStats packetStats = eventsByDevice.get(deviceId);
|
||||
if (packetStats == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder ret = new StringBuilder();
|
||||
|
||||
long timeInMillis = System.currentTimeMillis() - packetStats.createdAtMillis;
|
||||
if (timeInMillis > EVENT_KEEP_WINDOW_MILLIS) {
|
||||
timeInMillis = EVENT_KEEP_WINDOW_MILLIS;
|
||||
}
|
||||
long hours = TimeUnit.MILLISECONDS.toHours(timeInMillis);
|
||||
long minutes = TimeUnit.MILLISECONDS.toMinutes(timeInMillis) % 60;
|
||||
ret.append("From last ");
|
||||
ret.append(hours);
|
||||
ret.append("h ");
|
||||
ret.append(minutes);
|
||||
ret.append("m\n\n");
|
||||
|
||||
ArrayList<PacketStats.Summary> counts = new ArrayList<>(packetStats.getSummaries());
|
||||
Collections.sort(counts, (o1, o2) -> Integer.compare(o2.total, o1.total)); // Sort them by total number of events
|
||||
|
||||
for (PacketStats.Summary count : counts) {
|
||||
String name = count.packetType;
|
||||
if (name.startsWith("kdeconnect.")) {
|
||||
name = name.substring("kdeconnect.".length());
|
||||
}
|
||||
ret.append(name);
|
||||
ret.append("\n• ");
|
||||
ret.append(count.received);
|
||||
ret.append(" received\n• ");
|
||||
ret.append(count.sentSuccessful + count.sentFailed);
|
||||
ret.append(" sent (");
|
||||
ret.append(count.sentFailed);
|
||||
ret.append(" failed)\n");
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
public static void countReceived(@NonNull String deviceId, @NonNull String packetType) {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
|
||||
return; // computeIfAbsent not present in API < 24
|
||||
}
|
||||
synchronized (DeviceStats.class) {
|
||||
eventsByDevice
|
||||
.computeIfAbsent(deviceId, key -> new PacketStats())
|
||||
.receivedByType
|
||||
.computeIfAbsent(packetType, key -> new ArrayList<>())
|
||||
.add(System.currentTimeMillis());
|
||||
}
|
||||
cleanupIfNeeded();
|
||||
}
|
||||
|
||||
public static void countSent(@NonNull String deviceId, @NonNull String packetType, boolean success) {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
|
||||
return; // computeIfAbsent not present in API < 24
|
||||
}
|
||||
if (success) {
|
||||
synchronized (DeviceStats.class) {
|
||||
eventsByDevice
|
||||
.computeIfAbsent(deviceId, key -> new PacketStats())
|
||||
.sentSuccessfulByType
|
||||
.computeIfAbsent(packetType, key -> new ArrayList<>())
|
||||
.add(System.currentTimeMillis());
|
||||
}
|
||||
} else {
|
||||
synchronized (DeviceStats.class) {
|
||||
eventsByDevice
|
||||
.computeIfAbsent(deviceId, key -> new PacketStats())
|
||||
.sentFailedByType
|
||||
.computeIfAbsent(packetType, key -> new ArrayList<>())
|
||||
.add(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
cleanupIfNeeded();
|
||||
}
|
||||
|
||||
private static void cleanupIfNeeded() {
|
||||
final long cutoutTimestamp = System.currentTimeMillis() - EVENT_KEEP_WINDOW_MILLIS;
|
||||
if (System.currentTimeMillis() > nextCleanup) {
|
||||
synchronized (DeviceStats.class) {
|
||||
Log.i("PacketStats", "Doing periodic cleanup");
|
||||
for (PacketStats de : eventsByDevice.values()) {
|
||||
removeOldEvents(de.receivedByType, cutoutTimestamp);
|
||||
removeOldEvents(de.sentFailedByType, cutoutTimestamp);
|
||||
removeOldEvents(de.sentSuccessfulByType, cutoutTimestamp);
|
||||
}
|
||||
nextCleanup = System.currentTimeMillis() + CLEANUP_INTERVAL_MILLIS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static void removeOldEvents(HashMap<String, ArrayList<Long>> eventsByType, final long cutoutTimestamp) {
|
||||
|
||||
Iterator<Map.Entry<String, ArrayList<Long>>> iterator = eventsByType.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, ArrayList<Long>> entry = iterator.next();
|
||||
ArrayList<Long> events = entry.getValue();
|
||||
|
||||
int index = Collections.binarySearch(events, cutoutTimestamp);
|
||||
if (index < 0) {
|
||||
index = -(index + 1); // Convert the negative index to insertion point
|
||||
}
|
||||
|
||||
if (index < events.size()) {
|
||||
events.subList(0, index).clear();
|
||||
} else {
|
||||
iterator.remove(); // No element greater than the threshold
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -11,22 +11,14 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.univocity.parsers.csv.CsvParser;
|
||||
import com.univocity.parsers.csv.CsvParserSettings;
|
||||
import com.jaredrummler.android.device.DeviceName;
|
||||
|
||||
import org.kde.kdeconnect.Device;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -35,13 +27,10 @@ public class DeviceHelper {
|
||||
public static final int ProtocolVersion = 7;
|
||||
|
||||
public static final String KEY_DEVICE_NAME_PREFERENCE = "device_name_preference";
|
||||
public static final String KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET = "device_name_downloaded_preference";
|
||||
public static final String KEY_DEVICE_ID_PREFERENCE = "device_id_preference";
|
||||
|
||||
private static boolean fetchingName = false;
|
||||
|
||||
public static final String DEVICE_DATABASE = "https://storage.googleapis.com/play_public/supported_devices.csv";
|
||||
|
||||
private static boolean isTablet() {
|
||||
Configuration config = Resources.getSystem().getConfiguration();
|
||||
//This assumes that the values for the screen sizes are consecutive, so XXLARGE > XLARGE > LARGE
|
||||
@@ -63,55 +52,35 @@ public class DeviceHelper {
|
||||
}
|
||||
}
|
||||
|
||||
//It returns getAndroidDeviceName() if no user-defined name has been set with setDeviceName().
|
||||
public static String getDeviceName(Context context) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (!preferences.contains(KEY_DEVICE_NAME_PREFERENCE)
|
||||
&& !preferences.getBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, false)
|
||||
&& !fetchingName) {
|
||||
fetchingName = true;
|
||||
DeviceHelper.backgroundFetchDeviceName(context);
|
||||
return Build.MODEL;
|
||||
// Could use preferences.contains but would need to check for empty String anyway.
|
||||
String deviceName = preferences.getString(KEY_DEVICE_NAME_PREFERENCE, "");
|
||||
if (deviceName.isEmpty()) {
|
||||
//DeviceName.init(context); // Needed in DeviceName 2.x +
|
||||
if (!fetchingName) {
|
||||
fetchingName = true;
|
||||
DeviceHelper.backgroundFetchDeviceName(context); //Starts a background thread that will eventually update the shared pref
|
||||
}
|
||||
return DeviceName.getDeviceName(); //Temp name while we fetch it from the internet
|
||||
}
|
||||
return preferences.getString(KEY_DEVICE_NAME_PREFERENCE, Build.MODEL);
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
private static void backgroundFetchDeviceName(final Context context) {
|
||||
ThreadHelper.execute(() -> {
|
||||
try {
|
||||
URL url = new URL(DEVICE_DATABASE);
|
||||
URLConnection connection = url.openConnection();
|
||||
|
||||
// If we get here we managed to download the file. Mark that as done so we don't try again even if we don't end up finding a name.
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferences.edit().putBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, true).apply();
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_16))) {
|
||||
CsvParserSettings settings = new CsvParserSettings();
|
||||
settings.setHeaderExtractionEnabled(true);
|
||||
CsvParser parser = new CsvParser(settings);
|
||||
boolean found = false;
|
||||
for (String[] records : parser.iterate(reader)) {
|
||||
if (records.length < 4) {
|
||||
continue;
|
||||
}
|
||||
String buildModel = records[3];
|
||||
if (Build.MODEL.equals(buildModel)) {
|
||||
String deviceName = records[1];
|
||||
Log.i("DeviceHelper", "Got device name: " + deviceName);
|
||||
// Update the shared preference. Places that display the name should be listening to this change and update it
|
||||
setDeviceName(context, deviceName);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
Log.e("DeviceHelper", "Didn't find a device name for " + Build.MODEL);
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
DeviceName.with(context).request((info, error) -> {
|
||||
fetchingName = false;
|
||||
if (error != null) {
|
||||
Log.e("DeviceHelper", "Error fetching device name");
|
||||
error.printStackTrace();
|
||||
}
|
||||
if (info != null) {
|
||||
String deviceName = info.getName();
|
||||
Log.i("DeviceHelper", "Got device name: " + deviceName);
|
||||
// Update the shared preference. Places that display the name should be listening to this change and update it
|
||||
setDeviceName(context, deviceName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,7 @@ import android.os.Build;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import org.kde.kdeconnect.MyApplication;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class IntentHelper {
|
||||
@@ -26,8 +27,8 @@ public class IntentHelper {
|
||||
* @param intent the Intent to be started
|
||||
* @param title a title which is shown in the notification on Android 10+
|
||||
*/
|
||||
public static void startActivityFromBackgroundOrCreateNotification(Context context, Intent intent, String title) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !LifecycleHelper.isInForeground()) {
|
||||
public static void startActivityFromBackground(Context context, Intent intent, String title) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !MyApplication.isInForeground()) {
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
|
||||
Notification notification = new NotificationCompat
|
||||
.Builder(context, NotificationHelper.Channels.HIGHPRIORITY)
|
||||
|
@@ -14,6 +14,8 @@ import android.preference.PreferenceManager;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.RandomHelper;
|
||||
import org.bouncycastle.asn1.x500.RDN;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x500.X500NameBuilder;
|
||||
@@ -24,8 +26,6 @@ import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.RandomHelper;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -176,29 +176,26 @@ public class SslHelper {
|
||||
return !cert.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored certificate for a trusted device
|
||||
**/
|
||||
public static Certificate getDeviceCertificate(Context context, String deviceId) throws CertificateException {
|
||||
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0);
|
||||
return parseCertificate(certificateBytes);
|
||||
}
|
||||
|
||||
private static SSLContext getSslContextForDevice(Context context, String deviceId, boolean isDeviceTrusted) {
|
||||
private static SSLContext getSslContext(Context context, String deviceId, boolean isDeviceTrusted) {
|
||||
//TODO: Cache
|
||||
try {
|
||||
// Get device private key
|
||||
PrivateKey privateKey = RsaHelper.getPrivateKey(context);
|
||||
|
||||
// Get remote device certificate if trusted
|
||||
Certificate remoteDeviceCertificate = null;
|
||||
if (isDeviceTrusted) {
|
||||
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||
byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0);
|
||||
remoteDeviceCertificate = parseCertificate(certificateBytes);
|
||||
}
|
||||
|
||||
// Setup keystore
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keyStore.load(null, null);
|
||||
keyStore.setKeyEntry("key", privateKey, "".toCharArray(), new Certificate[]{certificate});
|
||||
|
||||
// Add device certificate if device trusted
|
||||
if (isDeviceTrusted) {
|
||||
Certificate remoteDeviceCertificate = getDeviceCertificate(context, deviceId);
|
||||
// Set certificate if device trusted
|
||||
if (remoteDeviceCertificate != null) {
|
||||
keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate);
|
||||
}
|
||||
|
||||
@@ -212,7 +209,7 @@ public class SslHelper {
|
||||
trustManagerFactory.init(keyStore);
|
||||
|
||||
// Setup custom trust manager if device not trusted
|
||||
SSLContext tlsContext = SSLContext.getInstance("TLSv1.2"); // Use TLS up to 1.2, since 1.3 seems to cause issues in some (older?) devices
|
||||
SSLContext tlsContext = SSLContext.getInstance("TLSv1"); //Newer TLS versions are only supported on API 16+
|
||||
if (isDeviceTrusted) {
|
||||
tlsContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), RandomHelper.secureRandom);
|
||||
} else {
|
||||
@@ -242,7 +239,7 @@ public class SslHelper {
|
||||
}
|
||||
|
||||
public static SSLSocket convertToSslSocket(Context context, Socket socket, String deviceId, boolean isDeviceTrusted, boolean clientMode) throws IOException {
|
||||
SSLSocketFactory sslsocketFactory = SslHelper.getSslContextForDevice(context, deviceId, isDeviceTrusted).getSocketFactory();
|
||||
SSLSocketFactory sslsocketFactory = SslHelper.getSslContext(context, deviceId, isDeviceTrusted).getSocketFactory();
|
||||
SSLSocket sslsocket = (SSLSocket) sslsocketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
|
||||
SslHelper.configureSslSocket(sslsocket, isDeviceTrusted, clientMode);
|
||||
return sslsocket;
|
||||
|