mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-09-05 08:35:10 +00:00
Compare commits
29 Commits
work/remov
...
v1.32.10
Author | SHA1 | Date | |
---|---|---|---|
|
125c9d54b3 | ||
|
5b937313ff | ||
|
2ad9f8eeb1 | ||
|
b3d84f31f4 | ||
|
5ca96fc378 | ||
|
05f1cbe136 | ||
|
d02e5aabb5 | ||
|
ae24cd6ca8 | ||
|
16414401c0 | ||
|
3f120fbea8 | ||
|
97806cf6b0 | ||
|
a923deee58 | ||
|
0923c8ecda | ||
|
172822239c | ||
|
6a58cc444e | ||
|
26667e4b78 | ||
|
086d366a1c | ||
|
84d380aee5 | ||
|
1ea956f5fb | ||
|
cfc7242db5 | ||
|
93b257d46c | ||
|
fa22722498 | ||
|
b0c9e46a31 | ||
|
53b49163d5 | ||
|
444f5725af | ||
|
1104baca8f | ||
|
c3af9b03f6 | ||
|
ecb38f2518 | ||
|
75ddac0bf0 |
@@ -7,9 +7,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:versionCode="13209"
|
||||
android:versionName="1.32.9">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
@@ -359,7 +357,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="org.kde.kdeconnect_tp.fileprovider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
|
@@ -21,7 +21,7 @@ buildscript {
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.kapt)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.dependencyLicenseReport)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
}
|
||||
@@ -50,8 +50,11 @@ android {
|
||||
namespace = "org.kde.kdeconnect_tp"
|
||||
compileSdk = 35
|
||||
defaultConfig {
|
||||
applicationId = "org.kde.kdeconnect_tp"
|
||||
minSdk = 21
|
||||
targetSdk = 35
|
||||
versionCode = 13210
|
||||
versionName = "1.32.10"
|
||||
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
|
||||
}
|
||||
buildFeatures {
|
||||
@@ -74,16 +77,18 @@ android {
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("main") {
|
||||
manifest.srcFile("AndroidManifest.xml")
|
||||
java.setSrcDirs(listOf("src"))
|
||||
resources.setSrcDirs(listOf("resources"))
|
||||
res.setSrcDirs(listOf(licenseResDir, "res"))
|
||||
assets.setSrcDirs(listOf("assets"))
|
||||
setRoot(".") // By default AGP expects all directories under src/main/...
|
||||
java.srcDir("src") // by default is "java"
|
||||
res.setSrcDirs(listOf(licenseResDir, "res")) // add licenseResDir
|
||||
}
|
||||
getByName("debug") {
|
||||
res.srcDir("dbg-res")
|
||||
}
|
||||
getByName("test") {
|
||||
java.setSrcDirs(listOf("tests"))
|
||||
java.srcDir("tests")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +110,8 @@ android {
|
||||
isMinifyEnabled = false
|
||||
isShrinkResources = false
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
applicationIdSuffix = ".debug"
|
||||
versionNameSuffix = "-debug"
|
||||
}
|
||||
getByName("release") {
|
||||
isMinifyEnabled = true
|
||||
@@ -254,6 +261,10 @@ abstract class FixCollectionsClassVisitorFactory :
|
||||
interface Params : InstrumentationParameters
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("com.albertvaka.classindexksp.annotations", "org.kde.kdeconnect.Plugins.PluginFactory.LoadablePlugin")
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
variant.instrumentation.transformClassesWith(
|
||||
@@ -307,8 +318,7 @@ dependencies {
|
||||
//implementation("com.github.bright:slf4android:0.1.6") { transitive = true } // For org.apache.sshd debugging
|
||||
implementation(libs.bcpkix.jdk15on) //For SSL certificate generation
|
||||
|
||||
implementation(libs.classindex)
|
||||
kapt(libs.classindex)
|
||||
ksp(libs.classindexksp)
|
||||
|
||||
// The android-smsmms library is the only way I know to handle MMS in Android
|
||||
// (Shouldn't a phone OS make phone things easy?)
|
||||
|
6
dbg-res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
6
dbg-res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- <background android:drawable="@drawable/ic_launcher_background"/>-->
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
5
dbg-res/mipmap-anydpi-v26/ic_launcher_banner.xml
Normal file
5
dbg-res/mipmap-anydpi-v26/ic_launcher_banner.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- <background android:drawable="@drawable/ic_launcher_banner_background"/>-->
|
||||
<foreground android:drawable="@drawable/ic_launcher_banner_foreground"/>
|
||||
</adaptive-icon>
|
6
dbg-res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
6
dbg-res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- <background android:drawable="@drawable/ic_launcher_background"/>-->
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
5
dbg-res/values-en-rGB/strings.xml
Normal file
5
dbg-res/values-en-rGB/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="kde_connect">Debug KDE Connect</string>
|
||||
</resources>
|
5
dbg-res/values/strings.xml
Normal file
5
dbg-res/values/strings.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="kde_connect">Debug KDE Connect</string>
|
||||
</resources>
|
14
fastlane/metadata/android/en-US/changelogs/13210.txt
Normal file
14
fastlane/metadata/android/en-US/changelogs/13210.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
1.32.10
|
||||
* Fixed app showing behind the notifications bar in Android 15
|
||||
* Fixed file transfers showing as failed when they succeeded
|
||||
* Fixed plugin list not updating after granting permissions
|
||||
|
||||
1.32.3
|
||||
* Fix trusted devices list
|
||||
|
||||
1.32.2
|
||||
* Handle expired certificates
|
||||
* Support doubletap drag in remote mouse
|
||||
|
||||
1.32.1
|
||||
* Fixed a crash when opening the presentation remote
|
@@ -1,13 +1,13 @@
|
||||
[versions]
|
||||
activityCompose = "1.9.3"
|
||||
androidDesugarJdkLibs = "2.1.3"
|
||||
androidGradlePlugin = "8.7.3"
|
||||
androidDesugarJdkLibs = "2.1.4"
|
||||
androidGradlePlugin = "8.8.0"
|
||||
androidSmsmms = "kdeconnect-1-21-0"
|
||||
appcompat = "1.7.0"
|
||||
bcpkixJdk15on = "1.70"
|
||||
classindex = "3.13"
|
||||
classindexksp = "1.1"
|
||||
commonsCollections4 = "4.4"
|
||||
commonsIo = "2.17.0"
|
||||
commonsIo = "2.18.0"
|
||||
commonsLang3 = "3.17.0"
|
||||
constraintlayoutCompose = "1.1.0"
|
||||
coreKtx = "1.15.0"
|
||||
@@ -17,16 +17,17 @@ documentfile = "1.0.1"
|
||||
gridlayout = "1.0.0"
|
||||
jsonassert = "1.5.3"
|
||||
junit = "4.13.2"
|
||||
kotlin = "2.0.21"
|
||||
kotlinxCoroutinesCore = "1.9.0"
|
||||
kotlin = "2.1.0"
|
||||
kspPlugin = "2.1.0-1.0.29"
|
||||
kotlinxCoroutinesCore = "1.10.1"
|
||||
lifecycleExtensions = "2.2.0"
|
||||
lifecycleRuntimeKtx = "2.8.7"
|
||||
logger = "1.0.3"
|
||||
material = "1.12.0"
|
||||
material3 = "1.3.1"
|
||||
media = "1.7.0"
|
||||
minaCore = "2.2.3"
|
||||
mockitoCore = "5.14.2"
|
||||
minaCore = "2.2.4"
|
||||
mockitoCore = "5.15.2"
|
||||
preferenceKtx = "1.2.1"
|
||||
reactiveStreams = "1.0.4"
|
||||
recyclerview = "1.3.2"
|
||||
@@ -34,7 +35,7 @@ rxjava = "2.2.21"
|
||||
sl4j = "2.0.13"
|
||||
sshdCore = "2.14.0"
|
||||
swiperefreshlayout = "1.1.0"
|
||||
uiToolingPreview = "1.7.5"
|
||||
uiToolingPreview = "1.7.6"
|
||||
univocityParsers = "2.9.1"
|
||||
|
||||
[libraries]
|
||||
@@ -58,7 +59,7 @@ androidx-preference-ktx = { module = "androidx.preference:preference-ktx", versi
|
||||
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
|
||||
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
|
||||
bcpkix-jdk15on = { module = "org.bouncycastle:bcpkix-jdk15on", version.ref = "bcpkixJdk15on" }
|
||||
classindex = { module = "org.atteo.classindex:classindex", version.ref = "classindex" }
|
||||
classindexksp = { module = "com.github.albertvaka:classindexksp", version.ref = "classindexksp" }
|
||||
commons-collections4 = { module = "org.apache.commons:commons-collections4", version.ref = "commonsCollections4" }
|
||||
commons-io = { module = "commons-io:commons-io", version.ref = "commonsIo" }
|
||||
commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "commonsLang3" }
|
||||
@@ -87,5 +88,5 @@ slf4j-handroid = { group = "com.gitlab.mvysny.slf4j", name = "slf4j-handroid", v
|
||||
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
|
||||
ksp = { id = "com.google.devtools.ksp", version.ref = "kspPlugin" }
|
||||
dependencyLicenseReport = { id = "com.github.jk1.dependency-license-report", version.ref = "dependencyLicenseReport" }
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,7 @@
|
||||
#Sat Sep 28 01:39:16 AM EDT 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
25
proguard-rules.pro
vendored
25
proguard-rules.pro
vendored
@@ -17,29 +17,10 @@
|
||||
#}
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
-dontwarn org.spongycastle.**
|
||||
-dontwarn org.apache.sshd.**
|
||||
-dontwarn org.apache.mina.**
|
||||
-dontwarn org.slf4j.**
|
||||
-dontwarn io.netty.**
|
||||
|
||||
-keepattributes SourceFile,LineNumberTable,Signature,*Annotation*
|
||||
|
||||
-keep class org.spongycastle.** {*;}
|
||||
|
||||
# SSHd requires mina, and mina uses reflection so some classes would get deleted
|
||||
-keep class org.apache.mina.** {*;}
|
||||
-keep class org.apache.sshd.** {*;}
|
||||
|
||||
-keep class org.kde.kdeconnect.** {*;}
|
||||
|
||||
-dontwarn org.mockito.**
|
||||
-dontwarn sun.reflect.**
|
||||
-dontwarn android.test.**
|
||||
-dontwarn java.lang.management.**
|
||||
-dontwarn javax.**
|
||||
# SSHd requires mina, and mina uses reflection so some classes would get deleted
|
||||
-keep class org.apache.sshd.** {*;}
|
||||
-dontwarn org.apache.sshd.**
|
||||
|
||||
-dontwarn android.net.ConnectivityManager
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||
-dontwarn android.net.LinkProperties
|
||||
|
@@ -21,6 +21,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:clipToPadding="false"
|
||||
android:id="@+id/scroll_view"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout
|
||||
|
@@ -26,6 +26,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/custom_device_item"/>
|
||||
|
||||
<TextView
|
||||
|
@@ -30,7 +30,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:id="@+id/device_view"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<!-- Shown when the device is paired and reachable -->
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
|
@@ -20,5 +20,6 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:id="@+id/licenses_text"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"/>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@@ -17,7 +17,8 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
<!-- Keep in sync with toolbar.xml, copied here because it needs the nested TabLayout -->
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
|
@@ -57,6 +57,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:dividerHeight="0dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:clipToPadding="false"
|
||||
tools:context=".MainActivity" />
|
||||
</LinearLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@@ -24,7 +24,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
app:drawableEndCompat="@drawable/ic_delete"
|
||||
app:drawableStartCompat="@drawable/ic_delete" />
|
||||
|
||||
<FrameLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/swipeableView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -32,17 +32,30 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
|
||||
<TextView
|
||||
android:id="@+id/deviceNameOrIP"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||
android:visibility="visible"
|
||||
tools:text="192.168.0.1"/>
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="192.168.0.1" />
|
||||
|
||||
</FrameLayout>
|
||||
<TextView
|
||||
android:id="@+id/connectionStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
@@ -22,6 +22,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:dividerHeight="12dp"
|
||||
android:orientation="vertical"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/list_card_entry"
|
||||
tools:context=".MainActivity" />
|
||||
|
||||
|
@@ -11,7 +11,9 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:fillViewport="true">
|
||||
android:fillViewport="true"
|
||||
android:clipToPadding="false"
|
||||
android:id="@+id/scroll_view">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/about_layout"
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">El nom del dispositiu no és vàlid</string>
|
||||
<string name="shareplugin_text_saved">S\'ha rebut text i s\'ha desat al porta-retalls</string>
|
||||
<string name="custom_devices_settings">Llista personalitzada de dispositius</string>
|
||||
<string name="custom_devices_settings_summary">S\'han afegir %d dispositius manualment</string>
|
||||
<string name="custom_device_list">Afegeix dispositius per la IP</string>
|
||||
<string name="custom_device_deleted">S\'ha suprimit un dispositiu personalitzat</string>
|
||||
<string name="custom_device_list_help">Si el dispositiu no es detecta automàticament, podeu afegir la seva adreça IP o el nom de la màquina fent clic al botó flotant d\'acció</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Continua reproduint</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Mostra una notificació silenciosa per a continuar reproduint en aquest dispositiu després de tancar l\'element multimèdia</string>
|
||||
<string name="notification_channel_keepwatching">Continua reproduint</string>
|
||||
<string name="ping_result">S\'ha fet «ping» en %1$d mil·lisegons</string>
|
||||
<string name="ping_failed">No s\'ha pogut fer «ping» al dispositiu</string>
|
||||
<string name="ping_in_progress">S\'està fent «ping»…</string>
|
||||
<string name="device_host_invalid">L\'ordinador no és vàlid. Useu un nom d\'ordinador, IPv4 o IPv6 vàlids</string>
|
||||
<string name="device_host_duplicate">L\'ordinador ja existeix a la llista</string>
|
||||
</resources>
|
||||
|
@@ -389,6 +389,8 @@
|
||||
<string name="send_compose">Sendi</string>
|
||||
<string name="compose_send_title">Verki sendon</string>
|
||||
<string name="open_compose_send">Verki tekston</string>
|
||||
<string name="double_tap_to_drag">Duoble frapetu por treni</string>
|
||||
<string name="hold_to_drag">Teni por treni</string>
|
||||
<string name="about_kde_about"><h1>Pri</h1> <p>KDE estas tutmonda komunumo de softvar-inĝenieroj, artistoj, verkistoj, tradukistoj kaj kreintoj kiuj engaĝiĝas al <a href=https://www.gnu.org/philosophy/free -sw.html>Disvolvado de Libera Programaro</a>. KDE produktas la Plasma labortablan medion, centojn da aplikaĵoj, kaj la multajn programarajn bibliotekojn kiuj subtenas ilin.</p> </p>KDE estas kunlabora entrepreno: neniu unuopa ento stiras ĝian direkton aŭ produktojn. Anstataŭe, ni kunlaboras por atingi la komunan celon konstrui la plej bonan Liberan Programaron de la mondo. Ĉiuj bonvenas <a href=https://community.kde.org/Get_Involved>aliiĝi kaj kontribui</a> al KDE, inkluzive de vi.</p> Vizitu <a href=https://www.kde.org/>https://www.kde.org/</a> por pliaj informoj pri la KDE-komunumo kaj la programaro, kiun ni produktas.</string>
|
||||
<string name="about_kde_report_bugs_or_wishes"><h1>Raporti Cimojn aŭ Dezirojn</h1> <p>Softvaro ĉiam povas esti plibonigita kaj la KDE-teamo pretas fari tion. Tamen, vi - la uzanto - devas diri al ni se io ne funkcias kiel atendite aŭ povus esti farata pli bone.</p> <p>KDE havas cimraportan sistemon. Vizitu <a href=https://bugs.kde.org/>https://bugs.kde.org/</a> aŭ uzu la butonon \"Raporti Cimon\" el la Pri-ekrano por raporti cimojn.</p> Se vi havas sugeston por plibonigo, vi estas bonvena uzi la cimtrakan sistemon pro registri vian deziron. Certigu, ke vi uzas la severecon nomita \"Wishlist\".</string>
|
||||
<string name="about_kde_join_kde"><h1>Kuniĝu al KDE</h1> <p>Vi ne devas esti programisto por esti membro de la teamo KDE. Vi povas aliĝi al la lingvoteamoj kiuj tradukas program-interfacojn. Vi povas provizi grafikaĵojn, etosojn, sonojn, kaj plibonigitan dokumentadon. Vi decidas!</p> <p>Vizitu <a href=https://community.kde.org/Get_Involved>https://community.kde.org/Get_Involved</a> por informo pri iuj projektoj en kiuj vi povas partopreni.</p> Se vi bezonas plian informon aŭ dorkmentadon, vizito al <a href=https://techbase.kde.org/>https://techbase.kde.org/</a> provizos al vi kion vi bezonas.</string>
|
||||
@@ -403,6 +405,7 @@
|
||||
<string name="maxim_leshchenko_task">UI-plibonigoj kaj ĉi tiu pri paĝo</string>
|
||||
<string name="holger_kaelberer_task">Fora klavara kromaĵo kaj korektoj de cimoj</string>
|
||||
<string name="saikrishna_arcot_task">Subteno por uzi la klavaron en la fora eniga kromaĵo, korektoj de cimoj kaj ĝeneralaj plibonigoj</string>
|
||||
<string name="shellwen_chen_task">Plibonigi la sekurecon de SFTP, plibonigi la funkciteneblon de ĉi projekto, cimkorektoj kaj ĝeneralaj plibonigoj</string>
|
||||
<string name="everyone_else">Ĉiuj aliaj, kiuj kontribuis al KDE Connect tra la jaroj</string>
|
||||
<string name="send_clipboard">Sendi tondujon</string>
|
||||
<string name="tap_to_execute">Frapi por plenumi</string>
|
||||
@@ -415,4 +418,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Daŭrigi ludadon</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Montri silentan sciigon por daŭrigi ludadon en ĉi tiu aparato post fermo de datumportilo</string>
|
||||
<string name="notification_channel_keepwatching">Daŭrigi ludadon</string>
|
||||
<string name="ping_result">Eĥosondis en %1$d milisekundoj</string>
|
||||
<string name="ping_failed">Ne eblis eĥosondi aparaton</string>
|
||||
<string name="ping_in_progress">Eĥosondante…</string>
|
||||
<string name="device_host_invalid">Gastiganto estas nevalida. Uzu validan gastigantnomon, IPv4, aŭ IPv6</string>
|
||||
<string name="device_host_duplicate">Gastiganto jam ekzistas en la listo</string>
|
||||
</resources>
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">Nombre de dispositivo no válido</string>
|
||||
<string name="shareplugin_text_saved">Texto recibido y guardado en el portapapeles</string>
|
||||
<string name="custom_devices_settings">Lista de dispositivos personalizada</string>
|
||||
<string name="custom_devices_settings_summary">%d dispositivos añadidos manualmente</string>
|
||||
<string name="custom_device_list">Añadir dispositivos por IP</string>
|
||||
<string name="custom_device_deleted">Dispositivo personalizado borrado</string>
|
||||
<string name="custom_device_list_help">Si su dispositivo no es detectado automáticamente puede añadir su dirección IP o nombre pulsando el botón de acción flotante</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Continuar reproduciendo</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Mostrar una notificación silenciosa para continuar reproduciendo en este dispositivo tras cerrar el reproductor.</string>
|
||||
<string name="notification_channel_keepwatching">Continuar reproduciendo</string>
|
||||
<string name="ping_result">Conectado en %1$d mili-segundos</string>
|
||||
<string name="ping_failed">No se pudo contactar con el dispositivo</string>
|
||||
<string name="ping_in_progress">Conectando</string>
|
||||
<string name="device_host_invalid">El servidor es inválido. Use un nombre válido de servidor, IPv4 o IPv6</string>
|
||||
<string name="device_host_duplicate">El servidor ya existe en la lista</string>
|
||||
</resources>
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">Gailuaren izen baliogabea</string>
|
||||
<string name="shareplugin_text_saved">Testua jaso da, arbelean kopiatu da</string>
|
||||
<string name="custom_devices_settings">Gailuen zerrenda pertsonalizatua</string>
|
||||
<string name="custom_devices_settings_summary">%d gailua eskuz gehitu dira</string>
|
||||
<string name="custom_device_list">Gehitu gailuak IP bidez</string>
|
||||
<string name="custom_device_deleted">Norberak finkatutako gailua ezabatu da</string>
|
||||
<string name="custom_device_list_help">Zure gailua ez bada automatikoki detektatzen bere IP helbidea edo ostalari-izena gehitu dezakezu ekintza botoi mugikorrean klik eginez</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Jarraitu jotzen</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Hedabidea itxi ondoren, gailu honetan jotzen jarraitzeko jakinarazpen ixil bat erakutsi.</string>
|
||||
<string name="notification_channel_keepwatching">Jarraitu jotzen</string>
|
||||
<string name="ping_result">«Ping» %1$d milisegundotan egin da</string>
|
||||
<string name="ping_failed">Ezin izan dio gailuari «ping» egin</string>
|
||||
<string name="ping_in_progress">«Ping» egiten...</string>
|
||||
<string name="device_host_invalid">Ostalaria baliogabea da. Erabili balio duen ostalari-izen bat, IPv4, edo IPv6</string>
|
||||
<string name="device_host_duplicate">Ostalaria jada zerrendan dago</string>
|
||||
</resources>
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">Nom de périphérique non valable</string>
|
||||
<string name="shareplugin_text_saved">Texte reçu et enregistré dans le presse-papiers</string>
|
||||
<string name="custom_devices_settings">Liste personnalisée de périphériques</string>
|
||||
<string name="custom_devices_settings_summary">%d périphériques ajoutés de façon manuelle</string>
|
||||
<string name="custom_device_list">Ajouter des périphériques par IP</string>
|
||||
<string name="custom_device_deleted">Périphérique personnalisé supprimé</string>
|
||||
<string name="custom_device_list_help">Si votre périphérique n\'est pas détecté automatiquement, vous pouvez ajouter son adresse IP ou son nom d\'hôte en cliquant sur le bouton d\'action flottant</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Continuer la lecture</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Afficher une notification silencieuse pour continuer à jouer sur ce périphérique après la fermeture du média.</string>
|
||||
<string name="notification_channel_keepwatching">Continuer la lecture</string>
|
||||
<string name="ping_result">Interrogé en %1$d millisecondes</string>
|
||||
<string name="ping_failed">Il est impossible d\'interroger un périphérique (Par ping)</string>
|
||||
<string name="ping_in_progress">Interrogation par ping en cours...</string>
|
||||
<string name="device_host_invalid">L\'hôte est non valable. Veuillez utiliser un nom d\'hôte valable, IPv4 ou IPv6</string>
|
||||
<string name="device_host_duplicate">L\'hôte existe déjà dans la liste.</string>
|
||||
</resources>
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">Nome de dispositivo incorrecto</string>
|
||||
<string name="shareplugin_text_saved">Recibiuse un texto e gardouse no portapapeis</string>
|
||||
<string name="custom_devices_settings">Lista de dispositivos personalizada</string>
|
||||
<string name="custom_devices_settings_summary">%d dispositivos engadidos manualmente</string>
|
||||
<string name="custom_device_list">Engadir dispositivos por IP</string>
|
||||
<string name="custom_device_deleted">Eliminouse o dispositivo personalizado</string>
|
||||
<string name="custom_device_list_help">Se o seu dispositivo non se detecta automaticamente pode engadir o seu enderezo IP ou nome de máquina premendo o botón flotante de acción</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Continuar reproducindo</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Amosar unha notificación silenciosa para continuar reproducindo neste dispositivo tras pechar o contido multimedia.</string>
|
||||
<string name="notification_channel_keepwatching">Continuar reproducindo</string>
|
||||
<string name="ping_result">Enviouse un ping en %1$d milisegundos</string>
|
||||
<string name="ping_failed">Non foi posíbel enviar un ping ao dispositivo.</string>
|
||||
<string name="ping_in_progress">Enviando un ping…</string>
|
||||
<string name="device_host_invalid">O servidor non é válido. Use un nome de servidor, enderezo IPv4 ou enderezo IPv6 válido.</string>
|
||||
<string name="device_host_duplicate">O servidor xa existe na lista.</string>
|
||||
</resources>
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">Nome dispositivo non valido</string>
|
||||
<string name="shareplugin_text_saved">Testo ricevuto, salvato negli appunti</string>
|
||||
<string name="custom_devices_settings">Elenco dispositivi personalizzati</string>
|
||||
<string name="custom_devices_settings_summary">%d dispositivi aggiunti manualmente</string>
|
||||
<string name="custom_device_list">Aggiungi dispositivi per IP</string>
|
||||
<string name="custom_device_deleted">Dispositivo personalizzato eliminato</string>
|
||||
<string name="custom_device_list_help">Se il tuo dispositivo non è rilevato automaticamente, puoi aggiungere il suo indirizzo IP o il nome host facendo clic sul pulsante Azione</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Continua la riproduzione</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Mostra una notifica silenziosa per continuare a giocare su questo dispositivo dopo aver chiuso il supporto multimediale</string>
|
||||
<string name="notification_channel_keepwatching">Continua la riproduzione</string>
|
||||
<string name="ping_result">Ping effettuato in %1$d millisecondi</string>
|
||||
<string name="ping_failed">Impossibile effettuare il ping del dispositivo</string>
|
||||
<string name="ping_in_progress">Ping in corso…</string>
|
||||
<string name="device_host_invalid">L\'host non è valido. Utilizza un nome host valido, IPv4 o IPv6</string>
|
||||
<string name="device_host_duplicate">L\'host esiste già nell\'elenco</string>
|
||||
</resources>
|
||||
|
@@ -213,6 +213,7 @@
|
||||
<string name="invalid_device_name">שם המכשיר שגוי</string>
|
||||
<string name="shareplugin_text_saved">התקבל טקסט, נשמר ללוח הגזירים</string>
|
||||
<string name="custom_devices_settings">רשימת מכשירים מותאמת אישית</string>
|
||||
<string name="custom_devices_settings_summary">%d התקנים נוספו ידנית</string>
|
||||
<string name="custom_device_list">הוספת מכשירים לפי IP</string>
|
||||
<string name="custom_device_deleted">מכשיר מותאם אישית נמחק</string>
|
||||
<string name="custom_device_list_help">אם המכשיר שלך לא מזוהה אוטומטית אפשר להוסיף את כתובת ה־IP או את שם המארח שלו בלחיצה על כפתור הפעולה הצף</string>
|
||||
@@ -436,4 +437,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">להמשיך לנגן</string>
|
||||
<string name="mpris_keepwatching_settings_summary">הצגת התראה שקטה כדי להמשיך לנגן בהתקן הזה לאחר סגירת המדיה</string>
|
||||
<string name="notification_channel_keepwatching">להמשיך לנגן</string>
|
||||
<string name="ping_result">הפינג ארך %1$d מילישניות</string>
|
||||
<string name="ping_failed">לא ניתן לשלוח פינג להתקן</string>
|
||||
<string name="ping_in_progress">נשלח פינג…</string>
|
||||
<string name="device_host_invalid">המארח שגוי. נא להשתמש בשם מארח, IPv4 או IPv6 תקניים.</string>
|
||||
<string name="device_host_duplicate">המארח כבר קיים ברשימה</string>
|
||||
</resources>
|
||||
|
@@ -412,4 +412,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">계속 재생</string>
|
||||
<string name="mpris_keepwatching_settings_summary">미디어를 닫은 후 이 장치에서 계속 재생할 수 있는 조용한 알림 표시</string>
|
||||
<string name="notification_channel_keepwatching">계속 재생</string>
|
||||
<string name="ping_result">핑 시간: %1$d 밀리초</string>
|
||||
<string name="ping_failed">장치에 핑을 보낼 수 없음</string>
|
||||
<string name="ping_in_progress">핑 진행 중…</string>
|
||||
<string name="device_host_invalid">호스트가 잘못되었습니다. 올바른 호스트 이름, IPv4, IPv6 주소를 지정하십시오</string>
|
||||
<string name="device_host_duplicate">호스트가 목록에 이미 있음</string>
|
||||
</resources>
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">Ongeldige apparaatnaam</string>
|
||||
<string name="shareplugin_text_saved">Oontvangen tekst, opgeslagen op klembord</string>
|
||||
<string name="custom_devices_settings">Aangepaste lijst apparaten</string>
|
||||
<string name="custom_devices_settings_summary">%d apparaten handmatig toegevoegd</string>
|
||||
<string name="custom_device_list">Voeg apparaten toe per IP-adres</string>
|
||||
<string name="custom_device_deleted">Aangepaste apparaat verwijderd</string>
|
||||
<string name="custom_device_list_help">Als uw apparaat niet automatisch wordt gedetecteerd kunt u zijn IP-adres of hostnaam toevoegen door te klikken op de knop Zwevende actie</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Doorgaan met afspelen</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Een stille melding tonen om door te gaan met afspelen op dit apparaat na sluiten van het medium</string>
|
||||
<string name="notification_channel_keepwatching">Doorgaan met afspelen</string>
|
||||
<string name="ping_result">Ping verkregen in %1$d milliseconden</string>
|
||||
<string name="ping_failed">Kreeg geen antwoord op ping van apparaat</string>
|
||||
<string name="ping_in_progress">Ping wordt verstuurt</string>
|
||||
<string name="device_host_invalid">Host is ongeldig. Gebruik een geldige hostnaam, IPv4 of IPv6</string>
|
||||
<string name="device_host_duplicate">Host bestaat al in de lijst</string>
|
||||
</resources>
|
||||
|
@@ -213,6 +213,7 @@
|
||||
<string name="invalid_device_name">Nieprawidłowa nazwa urządzenia</string>
|
||||
<string name="shareplugin_text_saved">Otrzymano tekst, zapisano do schowka</string>
|
||||
<string name="custom_devices_settings">Lista własnych urządzeń</string>
|
||||
<string name="custom_devices_settings_summary">%d urządzeń dodanych ręcznie</string>
|
||||
<string name="custom_device_list">Dodaj urządzenie po adresie IP</string>
|
||||
<string name="custom_device_deleted">Usunięto własne urządzenie</string>
|
||||
<string name="custom_device_list_help">Jeśli twoje urządzenie nie zostało wykryte samoczynnie, to możesz dodać je ręcznie po wpisaniu jego adresu IP lub nazwy gospodarza. Aby to zrobić, naciśnij pomarańczowy przycisk u dołu ekranu.</string>
|
||||
@@ -436,4 +437,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Kontynuuj odtwarzanie</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Pokaż ciche powiadomienie, aby kontynuować odtwarzanie na tym urządzeniu po zamknięciu mediów</string>
|
||||
<string name="notification_channel_keepwatching">Kontynuuj odtwarzanie</string>
|
||||
<string name="ping_result">Odpowiedział w %1$d milisekund</string>
|
||||
<string name="ping_failed">Nie można wysłać pingu do urządzenia</string>
|
||||
<string name="ping_in_progress">Wysyłanie pingu…</string>
|
||||
<string name="device_host_invalid">Nazwa gospodarza jest nieprawidłowa. Użyj prawidłowej nazwy, adresu IPv4 lub IPv6</string>
|
||||
<string name="device_host_duplicate">Nazwa gospodarza już znajduje się na liście</string>
|
||||
</resources>
|
||||
|
@@ -213,6 +213,7 @@
|
||||
<string name="invalid_device_name">Neveljavno ime naprave</string>
|
||||
<string name="shareplugin_text_saved">Prejeto besedilo shranjeno na odložišče</string>
|
||||
<string name="custom_devices_settings">Seznam naprav po meri</string>
|
||||
<string name="custom_devices_settings_summary">%d naprav, dodanih ročno</string>
|
||||
<string name="custom_device_list">Dodaj naprave po IP</string>
|
||||
<string name="custom_device_deleted">Zbrisana naprava po meri</string>
|
||||
<string name="custom_device_list_help">Če vaša naprava ni samodejno zaznana, lahko dodate njen IP naslov ali ime gostitelja s klikom na gumb Plavajoče dejanje</string>
|
||||
@@ -436,4 +437,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Nadaljuj s predvajanjem</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Pokaži tiho obvestilo za nadaljevanje predvajanja v tej napravi po zaprtju predstavnosti</string>
|
||||
<string name="notification_channel_keepwatching">Nadaljuj s predvajanjem</string>
|
||||
<string name="ping_result">Ping-an v %1$d milisekundah</string>
|
||||
<string name="ping_failed">Ni bilo mogoče pingati naprave</string>
|
||||
<string name="ping_in_progress">Pinganje…</string>
|
||||
<string name="device_host_invalid">Gostitelj ni veljaven. Uporabite veljavno ime gostitelja, IPv4 ali IPv6</string>
|
||||
<string name="device_host_duplicate">Gostitelj že obstaja na seznamu</string>
|
||||
</resources>
|
||||
|
@@ -197,6 +197,7 @@
|
||||
<string name="invalid_device_name">Geçersiz aygıt adı</string>
|
||||
<string name="shareplugin_text_saved">Gelen ileti, panoya kaydet</string>
|
||||
<string name="custom_devices_settings">Özel aygıt listesi</string>
|
||||
<string name="custom_devices_settings_summary">%d aygıt elle eklendi</string>
|
||||
<string name="custom_device_list">IP’ye göre aygıtları ekle</string>
|
||||
<string name="custom_device_deleted">Özel aygıt silindi</string>
|
||||
<string name="custom_device_list_help">Aygıtınız kendiliğinden algılanmazsa İşlem Düğmesine tıklayarak IP adresini veya ana bilgisayar adını ekleyebilirsiniz</string>
|
||||
@@ -420,4 +421,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Oynamayı Sürdür</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Ortamı kapattıktan sonra bu aygıtta oynatmayı sürdürmek için sessiz bir bildirim göster</string>
|
||||
<string name="notification_channel_keepwatching">Oynamayı Sürdür</string>
|
||||
<string name="ping_result">%1$d milisaniye içinde pinglendi</string>
|
||||
<string name="ping_failed">Aygıt pinglenemedi</string>
|
||||
<string name="ping_in_progress">Pingleniyor…</string>
|
||||
<string name="device_host_invalid">Makine geçersiz. Geçerli bir makine adı kullanın; IPv4 veya IPv6 gibi</string>
|
||||
<string name="device_host_duplicate">Listede makine halihazırda var</string>
|
||||
</resources>
|
||||
|
@@ -213,6 +213,7 @@
|
||||
<string name="invalid_device_name">Некоректна назва пристрою</string>
|
||||
<string name="shareplugin_text_saved">Отримано текст, збережено до буфера обміну даними</string>
|
||||
<string name="custom_devices_settings">Список нетипових пристроїв</string>
|
||||
<string name="custom_devices_settings_summary">%d пристроїв додано вручну</string>
|
||||
<string name="custom_device_list">Додати пристрої за IP</string>
|
||||
<string name="custom_device_deleted">Нетиповий пристрій вилучено</string>
|
||||
<string name="custom_device_list_help">Якщо ваш пристрій не було виявлено автоматично, ви можете додати його IP-адресу або назву вузла, натиснувши рухому кнопку дій.</string>
|
||||
@@ -436,4 +437,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">Продовжити відтворення</string>
|
||||
<string name="mpris_keepwatching_settings_summary">Показати беззвучне сповіщення для продовження відтворення на цьому пристрої після закриття носія</string>
|
||||
<string name="notification_channel_keepwatching">Продовжити відтворення</string>
|
||||
<string name="ping_result">Виконано зондування за %1$d мілісекунд</string>
|
||||
<string name="ping_failed">Не вдалося зондувати пристрій</string>
|
||||
<string name="ping_in_progress">Зондування…</string>
|
||||
<string name="device_host_invalid">Вузол є некоректним. Скористайтеся коректною назвою вузла, IPv4 або IPv6</string>
|
||||
<string name="device_host_duplicate">Запис вузла вже є у списку</string>
|
||||
</resources>
|
||||
|
@@ -189,6 +189,7 @@
|
||||
<string name="invalid_device_name">无效的设备名</string>
|
||||
<string name="shareplugin_text_saved">已收到文本,存至剪贴板</string>
|
||||
<string name="custom_devices_settings">自定义设备列表</string>
|
||||
<string name="custom_devices_settings_summary">手动添加了 %d 个设备</string>
|
||||
<string name="custom_device_list">通过 IP 添加设备</string>
|
||||
<string name="custom_device_deleted">自定义设备已删除</string>
|
||||
<string name="custom_device_list_help">如果您的设备未被自动检测到,您点击浮动操作按钮可以添加它的 IP 地址或主机名。</string>
|
||||
@@ -412,4 +413,9 @@
|
||||
<string name="mpris_keepwatching_settings_title">继续播放</string>
|
||||
<string name="mpris_keepwatching_settings_summary">关闭媒体后显示一条用于继续在此设备上播放的静音通知</string>
|
||||
<string name="notification_channel_keepwatching">继续播放</string>
|
||||
<string name="ping_result">Ping 的响应时间是 %1$d 毫秒</string>
|
||||
<string name="ping_failed">无法 ping 设备</string>
|
||||
<string name="ping_in_progress">正在执行 ping 命令…</string>
|
||||
<string name="device_host_invalid">主机无效。请使用有效的主机名、IPv4 或 IPv6 地址</string>
|
||||
<string name="device_host_duplicate">主机已在列表中</string>
|
||||
</resources>
|
||||
|
@@ -269,6 +269,7 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
<string name="invalid_device_name">Invalid device name</string>
|
||||
<string name="shareplugin_text_saved">Received text, saved to clipboard</string>
|
||||
<string name="custom_devices_settings">Custom device list</string>
|
||||
<string name="custom_devices_settings_summary">%d devices added manually</string>
|
||||
<string name="custom_device_list">Add devices by IP</string>
|
||||
<string name="custom_device_deleted">Custom device deleted</string>
|
||||
<string name="custom_device_list_help">If your device is not automatically detected you can add its IP address or hostname by clicking on the Floating Action Button</string>
|
||||
@@ -581,4 +582,11 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
<string name="mpris_keepwatching_settings_summary">Show a silent notification to continue playing on this device after closing media</string>
|
||||
<string name="notification_channel_keepwatching">Continue playing</string>
|
||||
|
||||
<string name="ping_result">Pinged in %1$d milliseconds</string>
|
||||
<string name="ping_failed">Could not ping device</string>
|
||||
<string name="ping_in_progress">Pinging…</string>
|
||||
|
||||
<string name="device_host_invalid">Host is invalid. Use a valid hostname, IPv4, or IPv6</string>
|
||||
<string name="device_host_duplicate">Host already exists in the list</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -3,11 +3,9 @@ pluginManagement {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
/* Needed for org.apache.sshd debugging
|
||||
maven {
|
||||
url = uri("https://jitpack.io")
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
|
@@ -20,6 +20,7 @@ import org.json.JSONException;
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.DeviceHost;
|
||||
import org.kde.kdeconnect.DeviceInfo;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
@@ -384,19 +385,19 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
|
||||
ThreadHelper.execute(() -> {
|
||||
List<String> ipStringList = CustomDevicesActivity
|
||||
.getCustomDeviceList(PreferenceManager.getDefaultSharedPreferences(context));
|
||||
List<DeviceHost> hostList = CustomDevicesActivity
|
||||
.getCustomDeviceList(context);
|
||||
|
||||
if (TrustedNetworkHelper.isTrustedNetwork(context)) {
|
||||
ipStringList.add("255.255.255.255"); //Default: broadcast.
|
||||
hostList.add(DeviceHost.BROADCAST); //Default: broadcast.
|
||||
} else {
|
||||
Log.i("LanLinkProvider", "Current network isn't trusted, not broadcasting");
|
||||
}
|
||||
|
||||
ArrayList<InetAddress> ipList = new ArrayList<>();
|
||||
for (String ip : ipStringList) {
|
||||
for (DeviceHost host : hostList) {
|
||||
try {
|
||||
ipList.add(InetAddress.getByName(ip));
|
||||
ipList.add(InetAddress.getByName(host.toString()));
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -470,7 +471,9 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
setupTcpListener();
|
||||
|
||||
mdnsDiscovery.startDiscovering();
|
||||
mdnsDiscovery.startAnnouncing();
|
||||
if (TrustedNetworkHelper.isTrustedNetwork(context)) {
|
||||
mdnsDiscovery.startAnnouncing();
|
||||
}
|
||||
|
||||
broadcastUdpIdentityPacket(null);
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@ import org.kde.kdeconnect.Backends.BaseLinkProvider
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider.ConnectionReceiver
|
||||
import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider
|
||||
import org.kde.kdeconnect.Backends.LoopbackBackend.LoopbackLinkProvider
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity
|
||||
@@ -50,7 +51,7 @@ import org.kde.kdeconnect_tp.R
|
||||
class BackgroundService : Service() {
|
||||
private lateinit var applicationInstance: KdeConnect
|
||||
|
||||
private val linkProviders = ArrayList<BaseLinkProvider>()
|
||||
private val linkProviders = mutableListOf<BaseLinkProvider>()
|
||||
|
||||
private val connectedToNonCellularNetwork = MutableLiveData<Boolean>()
|
||||
/** Indicates whether device is connected over wifi / usb / bluetooth / (anything other than cellular) */
|
||||
@@ -148,8 +149,8 @@ class BackgroundService : Service() {
|
||||
private fun createForegroundNotification(): Notification {
|
||||
// Why is this needed: https://developer.android.com/guide/components/services#Foreground
|
||||
|
||||
val connectedDevices = ArrayList<String>()
|
||||
val connectedDeviceIds = ArrayList<String>()
|
||||
val connectedDevices = mutableListOf<String>()
|
||||
val connectedDeviceIds = mutableListOf<String>()
|
||||
for (device in applicationInstance.devices.values) {
|
||||
if (device.isReachable && device.isPaired) {
|
||||
connectedDeviceIds.add(device.deviceId)
|
||||
|
65
src/org/kde/kdeconnect/DeviceHost.kt
Normal file
65
src/org/kde/kdeconnect/DeviceHost.kt
Normal file
@@ -0,0 +1,65 @@
|
||||
package org.kde.kdeconnect
|
||||
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper
|
||||
import java.net.InetAddress
|
||||
|
||||
class DeviceHost private constructor(private val host: String) {
|
||||
// Wrapper because Kotlin doesn't allow nested nullability
|
||||
data class PingResult(val latency: Long?)
|
||||
|
||||
/** The amount of milliseconds the ping request took or null it's in progress */
|
||||
var ping: PingResult? = null
|
||||
private set
|
||||
|
||||
/**
|
||||
* Checks if the host can be reached over the network.
|
||||
* @param callback Callback for updating UI elements
|
||||
*/
|
||||
fun checkReachable(callback: () -> Unit) {
|
||||
ThreadHelper.execute {
|
||||
try {
|
||||
val address = InetAddress.getByName(this.host)
|
||||
val startTime = System.currentTimeMillis()
|
||||
val pingable = address.isReachable(PING_TIMEOUT)
|
||||
val delayMillis = System.currentTimeMillis() - startTime
|
||||
val pingResult = PingResult(if (pingable) delayMillis else null)
|
||||
ping = pingResult
|
||||
}
|
||||
catch (_: Exception) {
|
||||
ping = PingResult(null)
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
require(isValidDeviceHost(host)) { "Invalid host" }
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return this.host
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Ping timeout */
|
||||
private const val PING_TIMEOUT = 3_000
|
||||
private val hostnameValidityPattern = Regex("^[0-9A-Za-z._-]+$")
|
||||
|
||||
@JvmStatic
|
||||
fun isValidDeviceHost(host: String): Boolean {
|
||||
return hostnameValidityPattern.matches(host)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toDeviceHostOrNull(host: String): DeviceHost? {
|
||||
return if (isValidDeviceHost(host)) {
|
||||
DeviceHost(host)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@JvmField
|
||||
val BROADCAST: DeviceHost = DeviceHost("255.255.255.255")
|
||||
}
|
||||
}
|
25
src/org/kde/kdeconnect/Helpers/WindowHelper.kt
Normal file
25
src/org/kde/kdeconnect/Helpers/WindowHelper.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Mash Kyrielight <fiepi@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Helpers
|
||||
|
||||
import android.view.View
|
||||
import org.kde.kdeconnect.extensions.setupBottomMargin
|
||||
import org.kde.kdeconnect.extensions.setupBottomPadding
|
||||
|
||||
object WindowHelper {
|
||||
|
||||
// for java only
|
||||
@JvmStatic
|
||||
fun setupBottomPadding(view: View) {
|
||||
view.setupBottomPadding()
|
||||
}
|
||||
|
||||
// for java only
|
||||
@JvmStatic
|
||||
fun setupBottomMargin(view: View) {
|
||||
view.setupBottomMargin()
|
||||
}
|
||||
}
|
@@ -15,37 +15,46 @@ import android.speech.RecognizerIntent;
|
||||
import android.speech.SpeechRecognizer;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect.UserInterface.PermissionsAlertDialogFragment;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityBigscreenBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
public class BigscreenActivity extends AppCompatActivity {
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
public class BigscreenActivity extends BaseActivity<ActivityBigscreenBinding> {
|
||||
|
||||
private static final int REQUEST_SPEECH = 100;
|
||||
|
||||
private final Lazy<ActivityBigscreenBinding> lazyBinding = LazyKt.lazy(() -> ActivityBigscreenBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ActivityBigscreenBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final ActivityBigscreenBinding binding = ActivityBigscreenBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar);
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
||||
|
||||
if (!SpeechRecognizer.isRecognitionAvailable(this)) {
|
||||
binding.micButton.setEnabled(false);
|
||||
binding.micButton.setVisibility(View.INVISIBLE);
|
||||
getBinding().micButton.setEnabled(false);
|
||||
getBinding().micButton.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
BigscreenPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, BigscreenPlugin.class);
|
||||
@@ -54,13 +63,13 @@ public class BigscreenActivity extends AppCompatActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
binding.leftButton.setOnClickListener(v -> plugin.sendLeft());
|
||||
binding.rightButton.setOnClickListener(v -> plugin.sendRight());
|
||||
binding.upButton.setOnClickListener(v -> plugin.sendUp());
|
||||
binding.downButton.setOnClickListener(v -> plugin.sendDown());
|
||||
binding.selectButton.setOnClickListener(v -> plugin.sendSelect());
|
||||
binding.homeButton.setOnClickListener(v -> plugin.sendHome());
|
||||
binding.micButton.setOnClickListener(v -> {
|
||||
getBinding().leftButton.setOnClickListener(v -> plugin.sendLeft());
|
||||
getBinding().rightButton.setOnClickListener(v -> plugin.sendRight());
|
||||
getBinding().upButton.setOnClickListener(v -> plugin.sendUp());
|
||||
getBinding().downButton.setOnClickListener(v -> plugin.sendDown());
|
||||
getBinding().selectButton.setOnClickListener(v -> plugin.sendSelect());
|
||||
getBinding().homeButton.setOnClickListener(v -> plugin.sendHome());
|
||||
getBinding().micButton.setOnClickListener(v -> {
|
||||
if (plugin.hasMicPermission()) {
|
||||
activateSTT();
|
||||
} else {
|
||||
|
@@ -10,26 +10,35 @@ import android.util.Log;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityFindMyPhoneBinding;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class FindMyPhoneActivity extends AppCompatActivity {
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
public class FindMyPhoneActivity extends BaseActivity<ActivityFindMyPhoneBinding> {
|
||||
static final String EXTRA_DEVICE_ID = "deviceId";
|
||||
|
||||
String deviceId;
|
||||
|
||||
private final Lazy<ActivityFindMyPhoneBinding> lazyBinding = LazyKt.lazy(() -> ActivityFindMyPhoneBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ActivityFindMyPhoneBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final ActivityFindMyPhoneBinding binding = ActivityFindMyPhoneBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar);
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
@@ -47,7 +56,7 @@ public class FindMyPhoneActivity extends AppCompatActivity {
|
||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
);
|
||||
|
||||
binding.bFindMyPhone.setOnClickListener(view -> finish());
|
||||
getBinding().bFindMyPhone.setOnClickListener(view -> finish());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -30,6 +30,7 @@ import org.kde.kdeconnect.UserInterface.compose.KdeTextButton
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTextField
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTheme
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTopAppBar
|
||||
import org.kde.kdeconnect.extensions.safeDrawPadding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
|
||||
private const val INPUT_CACHE_KEY = "compose_send_input_cache"
|
||||
@@ -91,6 +92,7 @@ class ComposeSendActivity : AppCompatActivity() {
|
||||
private fun ComposeSendScreen() {
|
||||
KdeTheme(this) {
|
||||
Scaffold(
|
||||
modifier = Modifier.safeDrawPadding(),
|
||||
topBar = {
|
||||
KdeTopAppBar(
|
||||
title = stringResource(R.string.compose_send_title),
|
||||
|
@@ -13,7 +13,6 @@ import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.Menu;
|
||||
@@ -24,18 +23,23 @@ import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityMousepadBinding;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
public class MousePadActivity
|
||||
extends AppCompatActivity
|
||||
extends BaseActivity<ActivityMousepadBinding>
|
||||
implements GestureDetector.OnGestureListener,
|
||||
GestureDetector.OnDoubleTapListener,
|
||||
MousePadGestureDetector.OnGestureListener,
|
||||
@@ -74,6 +78,14 @@ public class MousePadActivity
|
||||
|
||||
private boolean prefsApplied = false;
|
||||
|
||||
private final Lazy<ActivityMousepadBinding> lazyBinding = LazyKt.lazy(() -> ActivityMousepadBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ActivityMousepadBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
enum ClickType {
|
||||
LEFT, RIGHT, MIDDLE, NONE;
|
||||
|
||||
@@ -131,15 +143,12 @@ public class MousePadActivity
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_mousepad);
|
||||
|
||||
setSupportActionBar(findViewById(R.id.toolbar));
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
findViewById(R.id.mouse_click_left).setOnClickListener(v -> sendLeftClick());
|
||||
findViewById(R.id.mouse_click_middle).setOnClickListener(v -> sendMiddleClick());
|
||||
findViewById(R.id.mouse_click_right).setOnClickListener(v -> sendRightClick());
|
||||
getBinding().mouseClickLeft.setOnClickListener(v -> sendLeftClick());
|
||||
getBinding().mouseClickMiddle.setOnClickListener(v -> sendMiddleClick());
|
||||
getBinding().mouseClickRight.setOnClickListener(v -> sendRightClick());
|
||||
|
||||
deviceId = getIntent().getStringExtra("deviceId");
|
||||
|
||||
@@ -150,7 +159,7 @@ public class MousePadActivity
|
||||
mDetector.setOnDoubleTapListener(this);
|
||||
mSensorManager = ContextCompat.getSystemService(this, SensorManager.class);
|
||||
|
||||
keyListenerView = findViewById(R.id.keyListener);
|
||||
keyListenerView = getBinding().keyListener;
|
||||
keyListenerView.setDeviceId(deviceId);
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
@@ -582,9 +591,9 @@ public class MousePadActivity
|
||||
}
|
||||
|
||||
if (prefs.getBoolean(getString(R.string.mousepad_mouse_buttons_enabled_pref), true)) {
|
||||
findViewById(R.id.mouse_buttons).setVisibility(View.VISIBLE);
|
||||
getBinding().mouseButtons.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
findViewById(R.id.mouse_buttons).setVisibility(View.GONE);
|
||||
getBinding().mouseButtons.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
doubleTapDragEnabled = prefs.getBoolean(getString(R.string.mousepad_doubletap_drag_enabled_pref), true);
|
||||
|
@@ -13,17 +13,18 @@ import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.SafeTextChecker;
|
||||
import org.kde.kdeconnect.Helpers.WindowHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.UserInterface.List.EntryItemWithIcon;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivitySendkeystrokesBinding;
|
||||
|
||||
@@ -33,7 +34,10 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
public class SendKeystrokesToHostActivity extends BaseActivity<ActivitySendkeystrokesBinding> {
|
||||
|
||||
// text with these length and content can be send without user confirmation.
|
||||
// more or less chosen arbitrarily, so that we allow short PINS and TANS without interruption (if only one device is connected)
|
||||
@@ -42,19 +46,30 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
public static final String SAFE_CHARS = "1234567890";
|
||||
|
||||
|
||||
private ActivitySendkeystrokesBinding binding;
|
||||
private boolean contentIsOkay;
|
||||
|
||||
private final Lazy<ActivitySendkeystrokesBinding> lazyBinding = LazyKt.lazy(() -> ActivitySendkeystrokesBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ActivitySendkeystrokesBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isScrollable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivitySendkeystrokesBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar);
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
WindowHelper.setupBottomPadding(getBinding().devicesList);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +88,7 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
|
||||
if ("text/x-keystrokes".equals(type)) {
|
||||
String toSend = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
binding.textToSend.setText(toSend);
|
||||
getBinding().textToSend.setText(toSend);
|
||||
|
||||
// if the preference send_safe_text_immediately is true, we will check if exactly one
|
||||
// device is connected and send the text to it without user confirmation, to make sending of
|
||||
@@ -121,7 +136,7 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
|
||||
private void sendKeys(Device deviceId) {
|
||||
String toSend;
|
||||
if (binding.textToSend.getText() != null && (toSend = binding.textToSend.getText().toString().trim()).length() > 0) {
|
||||
if (getBinding().textToSend.getText() != null && (toSend = getBinding().textToSend.getText().toString().trim()).length() > 0) {
|
||||
final NetworkPacket np = new NetworkPacket(MousePadPlugin.PACKET_TYPE_MOUSEPAD_REQUEST);
|
||||
np.set("key", toSend);
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId.getDeviceId(), MousePadPlugin.class);
|
||||
@@ -156,8 +171,8 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
binding.devicesList.setAdapter(new ListAdapter(SendKeystrokesToHostActivity.this, items));
|
||||
binding.devicesList.setOnItemClickListener((adapterView, view, i, l) -> {
|
||||
getBinding().devicesList.setAdapter(new ListAdapter(SendKeystrokesToHostActivity.this, items));
|
||||
getBinding().devicesList.setOnItemClickListener((adapterView, view, i, l) -> {
|
||||
Device device = devicesList.get(i - 1); // NOTE: -1 because of the title!
|
||||
sendKeys(device);
|
||||
this.finish(); // close the activity
|
||||
|
@@ -8,22 +8,25 @@ package org.kde.kdeconnect.Plugins.MprisPlugin
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import org.kde.kdeconnect.Plugins.SystemVolumePlugin.SystemVolumeFragment
|
||||
import org.kde.kdeconnect.base.BaseActivity
|
||||
import org.kde.kdeconnect.extensions.viewBinding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityMprisBinding
|
||||
|
||||
class MprisActivity : AppCompatActivity() {
|
||||
private lateinit var activityMprisBinding: ActivityMprisBinding
|
||||
class MprisActivity : BaseActivity<ActivityMprisBinding>() {
|
||||
|
||||
override val binding: ActivityMprisBinding by viewBinding(ActivityMprisBinding::inflate)
|
||||
|
||||
private lateinit var mprisPagerAdapter: MprisPagerAdapter
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
return when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
||||
val pagePosition = activityMprisBinding.mprisTabs.selectedTabPosition
|
||||
val pagePosition = binding.mprisTabs.selectedTabPosition
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||
mprisPagerAdapter.onVolumeUp(pagePosition)
|
||||
} else {
|
||||
@@ -46,17 +49,13 @@ class MprisActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
activityMprisBinding = ActivityMprisBinding.inflate(layoutInflater)
|
||||
|
||||
setContentView(activityMprisBinding.root)
|
||||
|
||||
val deviceId = intent.getStringExtra(MprisPlugin.DEVICE_ID_KEY)
|
||||
|
||||
mprisPagerAdapter = MprisPagerAdapter(this, deviceId)
|
||||
activityMprisBinding.mprisPager.adapter = mprisPagerAdapter
|
||||
binding.mprisPager.adapter = mprisPagerAdapter
|
||||
|
||||
val tabLayoutMediator = TabLayoutMediator(
|
||||
activityMprisBinding.mprisTabs, activityMprisBinding.mprisPager
|
||||
binding.mprisTabs, binding.mprisPager
|
||||
) { tab, position ->
|
||||
tab.setText(
|
||||
mprisPagerAdapter.getTitle(position)
|
||||
@@ -65,7 +64,7 @@ class MprisActivity : AppCompatActivity() {
|
||||
|
||||
tabLayoutMediator.attach()
|
||||
|
||||
setSupportActionBar(activityMprisBinding.toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
|
@@ -3,14 +3,9 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Plugins.MprisPlugin
|
||||
|
||||
|
||||
package org.kde.kdeconnect.Plugins.MprisPlugin;
|
||||
|
||||
public interface VolumeKeyListener {
|
||||
|
||||
void onVolumeUp();
|
||||
|
||||
void onVolumeDown();
|
||||
|
||||
interface VolumeKeyListener {
|
||||
fun onVolumeUp()
|
||||
fun onVolumeDown()
|
||||
}
|
@@ -27,14 +27,15 @@ import android.widget.CheckBox;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
|
||||
import com.google.android.material.materialswitch.MaterialSwitch;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityNotificationFilterBinding;
|
||||
|
||||
@@ -43,9 +44,12 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
//TODO: Turn this into a PluginSettingsFragment
|
||||
public class NotificationFilterActivity extends AppCompatActivity {
|
||||
private ActivityNotificationFilterBinding binding;
|
||||
public class NotificationFilterActivity extends BaseActivity<ActivityNotificationFilterBinding> {
|
||||
|
||||
private AppDatabase appDatabase;
|
||||
private String prefKey;
|
||||
|
||||
@@ -60,6 +64,14 @@ public class NotificationFilterActivity extends AppCompatActivity {
|
||||
// This variable stores all app information and serves as a data source for filtering.
|
||||
private List<AppListInfo> mAllApps;
|
||||
private List<AppListInfo> apps; // Filtered data.
|
||||
|
||||
private final Lazy<ActivityNotificationFilterBinding> lazyBinding = LazyKt.lazy(() -> ActivityNotificationFilterBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ActivityNotificationFilterBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
class AppListAdapter extends BaseAdapter {
|
||||
|
||||
@@ -87,13 +99,13 @@ public class NotificationFilterActivity extends AppCompatActivity {
|
||||
if (position == 0) {
|
||||
checkedTextView.setText(R.string.all);
|
||||
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(checkedTextView, null, null, null, null);
|
||||
binding.lvFilterApps.setItemChecked(position, appDatabase.getAllEnabled());
|
||||
getBinding().lvFilterApps.setItemChecked(position, appDatabase.getAllEnabled());
|
||||
} else {
|
||||
final AppListInfo info = apps.get(position - 1);
|
||||
checkedTextView.setText(info.name);
|
||||
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(checkedTextView, info.icon, null, null, null);
|
||||
checkedTextView.setCompoundDrawablePadding((int) (8 * getResources().getDisplayMetrics().density));
|
||||
binding.lvFilterApps.setItemChecked(position, info.isEnabled);
|
||||
getBinding().lvFilterApps.setItemChecked(position, info.isEnabled);
|
||||
}
|
||||
|
||||
return view;
|
||||
@@ -105,14 +117,12 @@ public class NotificationFilterActivity extends AppCompatActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityNotificationFilterBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
appDatabase = new AppDatabase(NotificationFilterActivity.this, false);
|
||||
if (getIntent()!= null){
|
||||
prefKey = getIntent().getStringExtra(NotificationsPlugin.getPrefKey());
|
||||
}
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar);
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
SharedPreferences preferences = this.getSharedPreferences(prefKey, Context.MODE_PRIVATE);
|
||||
@@ -152,7 +162,7 @@ public class NotificationFilterActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void displayAppList() {
|
||||
final ListView listView = binding.lvFilterApps;
|
||||
final ListView listView = getBinding().lvFilterApps;
|
||||
AppListAdapter adapter = new AppListAdapter();
|
||||
listView.setAdapter(adapter);
|
||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
@@ -233,7 +243,7 @@ public class NotificationFilterActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
listView.setVisibility(View.VISIBLE);
|
||||
binding.spinner.setVisibility(View.GONE);
|
||||
getBinding().spinner.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private Drawable resizeIcon(Drawable icon, int maxSize) {
|
||||
@@ -284,7 +294,7 @@ public class NotificationFilterActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
((AppListAdapter) binding.lvFilterApps.getAdapter()).notifyDataSetChanged();
|
||||
((AppListAdapter) getBinding().lvFilterApps.getAdapter()).notifyDataSetChanged();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@@ -8,17 +8,17 @@ package org.kde.kdeconnect.Plugins
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.annotation.DrawableRes
|
||||
import org.atteo.classindex.ClassIndex
|
||||
import org.atteo.classindex.IndexAnnotated
|
||||
import org.kde.kdeconnect.Device
|
||||
|
||||
object PluginFactory {
|
||||
annotation class LoadablePlugin //Annotate plugins with this so PluginFactory finds them
|
||||
|
||||
private var pluginInfo: Map<String, PluginInfo> = mapOf()
|
||||
|
||||
fun initPluginInfo(context: Context) {
|
||||
try {
|
||||
val plugins = ClassIndex.getAnnotated(LoadablePlugin::class.java)
|
||||
.map { it.newInstance() as Plugin }
|
||||
val plugins = com.albertvaka.classindexksp.LoadablePlugin
|
||||
.map { it.java.getDeclaredConstructor().newInstance() as Plugin }
|
||||
.map { plugin -> plugin.apply { setContext(context, null) } }
|
||||
|
||||
pluginInfo = plugins.associate { plugin -> Pair(plugin.pluginKey, PluginInfo(plugin)) }
|
||||
@@ -45,7 +45,7 @@ object PluginFactory {
|
||||
|
||||
fun instantiatePluginForDevice(context: Context, pluginKey: String, device: Device): Plugin? {
|
||||
try {
|
||||
val plugin = pluginInfo[pluginKey]?.instantiableClass?.newInstance()?.apply { setContext(context, device) }
|
||||
val plugin = pluginInfo[pluginKey]?.instantiableClass?.getDeclaredConstructor()?.newInstance()?.apply { setContext(context, device) }
|
||||
return plugin
|
||||
} catch (e: Exception) {
|
||||
Log.e("PluginFactory", "Could not instantiate plugin: $pluginKey", e)
|
||||
@@ -67,9 +67,6 @@ object PluginFactory {
|
||||
return used.map { it.key }.toSet()
|
||||
}
|
||||
|
||||
@IndexAnnotated
|
||||
annotation class LoadablePlugin //Annotate plugins with this so PluginFactory finds them
|
||||
|
||||
class PluginInfo private constructor(
|
||||
val displayName: String,
|
||||
val description: String,
|
||||
|
@@ -37,6 +37,7 @@ import org.kde.kdeconnect.KdeConnect
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeButton
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTheme
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTopAppBar
|
||||
import org.kde.kdeconnect.extensions.safeDrawPadding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
|
||||
private const val VOLUME_UP = 1
|
||||
@@ -116,7 +117,10 @@ class PresenterActivity : AppCompatActivity(), SensorEventListener {
|
||||
val sensorManager = LocalContext.current.getSystemService(SENSOR_SERVICE) as? SensorManager
|
||||
|
||||
KdeTheme(this) {
|
||||
Scaffold(topBar = { PresenterAppBar() }) {
|
||||
Scaffold(
|
||||
modifier = Modifier.safeDrawPadding(),
|
||||
topBar = { PresenterAppBar() }
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().padding(it).padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp),
|
||||
|
@@ -17,8 +17,8 @@ import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.json.JSONException;
|
||||
@@ -26,6 +26,7 @@ import org.json.JSONObject;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityRunCommandBinding;
|
||||
|
||||
@@ -35,8 +36,19 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class RunCommandActivity extends AppCompatActivity {
|
||||
private ActivityRunCommandBinding binding;
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
public class RunCommandActivity extends BaseActivity<ActivityRunCommandBinding> {
|
||||
|
||||
private final Lazy<ActivityRunCommandBinding> lazyBinding = LazyKt.lazy(() -> ActivityRunCommandBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ActivityRunCommandBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
private String deviceId;
|
||||
private final RunCommandPlugin.CommandsChangedCallback commandsChangedCallback = () -> runOnUiThread(this::updateView);
|
||||
private List<CommandEntry> commandItems;
|
||||
@@ -48,7 +60,7 @@ public class RunCommandActivity extends AppCompatActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
registerForContextMenu(binding.runCommandsList);
|
||||
registerForContextMenu(getBinding().runCommandsList);
|
||||
|
||||
commandItems = new ArrayList<>();
|
||||
for (JSONObject obj : plugin.getCommandList()) {
|
||||
@@ -63,26 +75,23 @@ public class RunCommandActivity extends AppCompatActivity {
|
||||
|
||||
ListAdapter adapter = new ListAdapter(RunCommandActivity.this, commandItems);
|
||||
|
||||
binding.runCommandsList.setAdapter(adapter);
|
||||
binding.runCommandsList.setOnItemClickListener((adapterView, view1, i, l) ->
|
||||
getBinding().runCommandsList.setAdapter(adapter);
|
||||
getBinding().runCommandsList.setOnItemClickListener((adapterView, view1, i, l) ->
|
||||
plugin.runCommand(commandItems.get(i).getKey()));
|
||||
|
||||
String text = getString(R.string.addcommand_explanation);
|
||||
if (!plugin.canAddCommand()) {
|
||||
text += "\n" + getString(R.string.addcommand_explanation2);
|
||||
}
|
||||
binding.addCommandExplanation.setText(text);
|
||||
binding.addCommandExplanation.setVisibility(commandItems.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
getBinding().addCommandExplanation.setText(text);
|
||||
getBinding().addCommandExplanation.setVisibility(commandItems.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityRunCommandBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar);
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
@@ -93,11 +102,11 @@ public class RunCommandActivity extends AppCompatActivity {
|
||||
RunCommandPlugin plugin = device.getPlugin(RunCommandPlugin.class);
|
||||
if (plugin != null) {
|
||||
if (plugin.canAddCommand()) {
|
||||
binding.addCommandButton.show();
|
||||
getBinding().addCommandButton.show();
|
||||
} else {
|
||||
binding.addCommandButton.hide();
|
||||
getBinding().addCommandButton.hide();
|
||||
}
|
||||
binding.addCommandButton.setOnClickListener(v -> {
|
||||
getBinding().addCommandButton.setOnClickListener(v -> {
|
||||
plugin.sendSetupPacket();
|
||||
new AlertDialog.Builder(RunCommandActivity.this)
|
||||
.setTitle(R.string.add_command)
|
||||
|
@@ -23,6 +23,7 @@ import androidx.core.content.FileProvider;
|
||||
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect_tp.BuildConfig;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.io.File;
|
||||
@@ -145,7 +146,7 @@ class ReceiveNotification {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && "file".equals(destinationUri.getScheme())) {
|
||||
//Nougat and later require "content://" uris instead of "file://" uris
|
||||
File file = new File(destinationUri.getPath());
|
||||
Uri contentUri = FileProvider.getUriForFile(device.getContext(), "org.kde.kdeconnect_tp.fileprovider", file);
|
||||
Uri contentUri = FileProvider.getUriForFile(device.getContext(), BuildConfig.APPLICATION_ID+".fileprovider", file);
|
||||
intent.setDataAndType(contentUri, mimeType);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
||||
|
@@ -15,16 +15,19 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.WindowHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.List.EntryItemWithIcon;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityShareBinding;
|
||||
|
||||
@@ -34,11 +37,27 @@ import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class ShareActivity extends AppCompatActivity {
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
public class ShareActivity extends BaseActivity<ActivityShareBinding> {
|
||||
private static final String KEY_UNREACHABLE_URL_LIST = "key_unreachable_url_list";
|
||||
private ActivityShareBinding binding;
|
||||
|
||||
private SharedPreferences mSharedPrefs;
|
||||
|
||||
private final Lazy<ActivityShareBinding> lazyBinding = LazyKt.lazy(() -> ActivityShareBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ActivityShareBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isScrollable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
@@ -59,9 +78,9 @@ public class ShareActivity extends AppCompatActivity {
|
||||
private void refreshDevicesAction() {
|
||||
BackgroundService.ForceRefreshConnections(this);
|
||||
|
||||
binding.devicesListLayout.refreshListLayout.setRefreshing(true);
|
||||
binding.devicesListLayout.refreshListLayout.postDelayed(() -> {
|
||||
binding.devicesListLayout.refreshListLayout.setRefreshing(false);
|
||||
getBinding().devicesListLayout.refreshListLayout.setRefreshing(true);
|
||||
getBinding().devicesListLayout.refreshListLayout.postDelayed(() -> {
|
||||
getBinding().devicesListLayout.refreshListLayout.setRefreshing(false);
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
@@ -100,8 +119,8 @@ public class ShareActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
binding.devicesListLayout.devicesList.setAdapter(new ListAdapter(ShareActivity.this, items));
|
||||
binding.devicesListLayout.devicesList.setOnItemClickListener((adapterView, view, i, l) -> {
|
||||
getBinding().devicesListLayout.devicesList.setAdapter(new ListAdapter(ShareActivity.this, items));
|
||||
getBinding().devicesListLayout.devicesList.setOnItemClickListener((adapterView, view, i, l) -> {
|
||||
Device device = devicesList.get(i - 1); //NOTE: -1 because of the title!
|
||||
SharePlugin plugin = KdeConnect.getInstance().getDevicePlugin(device.getDeviceId(), SharePlugin.class);
|
||||
if (intentHasUrl && !device.isReachable()) {
|
||||
@@ -140,20 +159,19 @@ public class ShareActivity extends AppCompatActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityShareBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences (this);
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar);
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
binding.devicesListLayout.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
|
||||
getBinding().devicesListLayout.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
}
|
||||
|
||||
WindowHelper.setupBottomPadding(getBinding().devicesListLayout.devicesList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -18,11 +18,13 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity
|
||||
import org.kde.kdeconnect.extensions.setupBottomPadding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.FragmentAboutBinding
|
||||
|
||||
class AboutFragment : Fragment() {
|
||||
private var binding: FragmentAboutBinding? = null
|
||||
private var _binding: FragmentAboutBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private lateinit var aboutData: AboutData
|
||||
private var tapCount = 0
|
||||
private var firstTapMillis: Long? = null
|
||||
@@ -46,23 +48,28 @@ class AboutFragment : Fragment() {
|
||||
}
|
||||
|
||||
aboutData = requireArguments().getParcelable("ABOUT_DATA")!!
|
||||
binding = FragmentAboutBinding.inflate(inflater, container, false)
|
||||
_binding = FragmentAboutBinding.inflate(inflater, container, false)
|
||||
|
||||
updateData()
|
||||
return binding!!.root
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.scrollView.setupBottomPadding()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun updateData() {
|
||||
// Update general info
|
||||
|
||||
binding!!.appName.text = aboutData.name
|
||||
binding!!.appIcon.setImageDrawable(this.context?.let { ContextCompat.getDrawable(it, aboutData.icon) })
|
||||
binding!!.appVersion.text = this.context?.getString(R.string.version, aboutData.versionName)
|
||||
binding.appName.text = aboutData.name
|
||||
binding.appIcon.setImageDrawable(this.context?.let { ContextCompat.getDrawable(it, aboutData.icon) })
|
||||
binding.appVersion.text = this.context?.getString(R.string.version, aboutData.versionName)
|
||||
|
||||
// Setup Easter Egg onClickListener
|
||||
|
||||
binding!!.generalInfoCard.setOnClickListener {
|
||||
binding.generalInfoCard.setOnClickListener {
|
||||
if (firstTapMillis == null) {
|
||||
firstTapMillis = System.currentTimeMillis()
|
||||
}
|
||||
@@ -80,24 +87,24 @@ class AboutFragment : Fragment() {
|
||||
|
||||
// Update button onClickListeners
|
||||
|
||||
setupInfoButton(aboutData.bugURL, binding!!.reportBugButton)
|
||||
setupInfoButton(aboutData.donateURL, binding!!.donateButton)
|
||||
setupInfoButton(aboutData.sourceCodeURL, binding!!.sourceCodeButton)
|
||||
setupInfoButton(aboutData.bugURL, binding.reportBugButton)
|
||||
setupInfoButton(aboutData.donateURL, binding.donateButton)
|
||||
setupInfoButton(aboutData.sourceCodeURL, binding.sourceCodeButton)
|
||||
|
||||
binding!!.licensesButton.setOnClickListener {
|
||||
binding.licensesButton.setOnClickListener {
|
||||
startActivity(Intent(context, LicensesActivity::class.java))
|
||||
}
|
||||
|
||||
binding!!.aboutKdeButton.setOnClickListener {
|
||||
binding.aboutKdeButton.setOnClickListener {
|
||||
startActivity(Intent(context, AboutKDEActivity::class.java))
|
||||
}
|
||||
|
||||
setupInfoButton(aboutData.websiteURL, binding!!.websiteButton)
|
||||
setupInfoButton(aboutData.websiteURL, binding.websiteButton)
|
||||
|
||||
// Update authors
|
||||
binding!!.authorsList.adapter = ListAdapter(this.requireContext(), aboutData.authors.map { AboutPersonEntryItem(it) }, false)
|
||||
binding.authorsList.adapter = ListAdapter(this.requireContext(), aboutData.authors.map { AboutPersonEntryItem(it) }, false)
|
||||
if (aboutData.authorsFooterText != null) {
|
||||
binding!!.authorsFooterText.text = context?.getString(aboutData.authorsFooterText!!)
|
||||
binding.authorsFooterText.text = context?.getString(aboutData.authorsFooterText!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +120,6 @@ class AboutFragment : Fragment() {
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
binding = null
|
||||
_binding = null
|
||||
}
|
||||
}
|
@@ -10,17 +10,21 @@ import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.text.Spanned
|
||||
import android.text.method.LinkMovementMethod
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kde.kdeconnect.base.BaseActivity
|
||||
import org.kde.kdeconnect.extensions.setupBottomPadding
|
||||
import org.kde.kdeconnect.extensions.viewBinding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityAboutKdeBinding
|
||||
|
||||
class AboutKDEActivity : AppCompatActivity() {
|
||||
class AboutKDEActivity : BaseActivity<ActivityAboutKdeBinding>() {
|
||||
|
||||
override val binding: ActivityAboutKdeBinding by viewBinding(ActivityAboutKdeBinding::inflate)
|
||||
|
||||
override val isScrollable: Boolean = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val binding = ActivityAboutKdeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar)
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar!!.setDisplayShowHomeEnabled(true)
|
||||
@@ -34,6 +38,8 @@ class AboutKDEActivity : AppCompatActivity() {
|
||||
binding.reportBugsOrWishesTextView.movementMethod = LinkMovementMethod.getInstance()
|
||||
binding.joinKdeTextView.movementMethod = LinkMovementMethod.getInstance()
|
||||
binding.supportKdeTextView.movementMethod = LinkMovementMethod.getInstance()
|
||||
|
||||
binding.scrollView.setupBottomPadding()
|
||||
}
|
||||
|
||||
private fun fromHtml(html: String): Spanned {
|
||||
|
@@ -10,23 +10,26 @@ import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.kde.kdeconnect.base.BaseActivity
|
||||
import org.kde.kdeconnect.extensions.setupBottomPadding
|
||||
import org.kde.kdeconnect.extensions.viewBinding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityLicensesBinding
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class LicensesActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityLicensesBinding
|
||||
class LicensesActivity : BaseActivity<ActivityLicensesBinding>() {
|
||||
|
||||
override val binding: ActivityLicensesBinding by viewBinding(ActivityLicensesBinding::inflate)
|
||||
|
||||
override val isScrollable: Boolean = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityLicensesBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.licensesText.setupBottomPadding()
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar)
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar!!.setDisplayShowHomeEnabled(true)
|
||||
|
@@ -7,15 +7,16 @@
|
||||
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.TooltipCompat;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
@@ -25,17 +26,21 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import org.kde.kdeconnect.DeviceHost;
|
||||
import org.kde.kdeconnect.Helpers.WindowHelper;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityCustomDevicesBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
//TODO: Require wifi connection so entries can be verified
|
||||
//TODO: Resolve to ip address and don't allow unresolvable or duplicates based on ip address
|
||||
//TODO: Sort the list
|
||||
public class CustomDevicesActivity extends AppCompatActivity implements CustomDevicesAdapter.Callback {
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
import kotlin.Unit;
|
||||
|
||||
public class CustomDevicesActivity extends BaseActivity<ActivityCustomDevicesBinding> implements CustomDevicesAdapter.Callback {
|
||||
private static final String TAG_ADD_DEVICE_DIALOG = "AddDeviceDialog";
|
||||
|
||||
private static final String KEY_CUSTOM_DEVLIST_PREFERENCE = "device_list_preference";
|
||||
@@ -45,37 +50,43 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
private RecyclerView recyclerView;
|
||||
private TextView emptyListMessage;
|
||||
|
||||
private ArrayList<String> customDeviceList;
|
||||
private ArrayList<DeviceHost> customDeviceList;
|
||||
private EditTextAlertDialogFragment addDeviceDialog;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private CustomDevicesAdapter customDevicesAdapter;
|
||||
private DeletedCustomDevice lastDeletedCustomDevice;
|
||||
private int editingDeviceAtPosition;
|
||||
|
||||
private final Lazy<ActivityCustomDevicesBinding> lazyBinding = LazyKt.lazy(() -> ActivityCustomDevicesBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ActivityCustomDevicesBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final ActivityCustomDevicesBinding binding = ActivityCustomDevicesBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
recyclerView = getBinding().recyclerView;
|
||||
emptyListMessage = getBinding().emptyListMessage;
|
||||
final FloatingActionButton fab = getBinding().floatingActionButton;
|
||||
|
||||
recyclerView = binding.recyclerView;
|
||||
emptyListMessage = binding.emptyListMessage;
|
||||
final FloatingActionButton fab = binding.floatingActionButton;
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar);
|
||||
setSupportActionBar(getBinding().toolbarLayout.toolbar);
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
fab.setOnClickListener(v -> showEditTextDialog(""));
|
||||
fab.setOnClickListener(v -> showEditTextDialog(null));
|
||||
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
customDeviceList = getCustomDeviceList(sharedPreferences);
|
||||
customDeviceList = getCustomDeviceList(this);
|
||||
customDeviceList.forEach(host -> host.checkReachable(() -> {
|
||||
runOnUiThread(() -> customDevicesAdapter.notifyDataSetChanged());
|
||||
return Unit.INSTANCE;
|
||||
}));
|
||||
|
||||
showEmptyListMessageIfRequired();
|
||||
|
||||
customDevicesAdapter = new CustomDevicesAdapter(this);
|
||||
customDevicesAdapter = new CustomDevicesAdapter(this, getApplicationContext());
|
||||
customDevicesAdapter.setCustomDevices(customDeviceList);
|
||||
|
||||
recyclerView.setHasFixedSize(true);
|
||||
@@ -83,6 +94,9 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
|
||||
recyclerView.setAdapter(customDevicesAdapter);
|
||||
|
||||
WindowHelper.setupBottomPadding(recyclerView);
|
||||
WindowHelper.setupBottomMargin(getBinding().floatingActionButton);
|
||||
|
||||
addDeviceDialog = (EditTextAlertDialogFragment) getSupportFragmentManager().findFragmentByTag(TAG_ADD_DEVICE_DIALOG);
|
||||
if (addDeviceDialog != null) {
|
||||
addDeviceDialog.setCallback(new AddDeviceDialogCallback());
|
||||
@@ -108,7 +122,11 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
emptyListMessage.setVisibility(customDeviceList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void showEditTextDialog(@NonNull String text) {
|
||||
private void showEditTextDialog(DeviceHost deviceHost) {
|
||||
String text = "";
|
||||
if (deviceHost != null) {
|
||||
text = deviceHost.toString();
|
||||
}
|
||||
addDeviceDialog = new EditTextAlertDialogFragment.Builder()
|
||||
.setTitle(R.string.add_device_dialog_title)
|
||||
.setHint(R.string.add_device_hint)
|
||||
@@ -122,6 +140,7 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
}
|
||||
|
||||
private void saveList() {
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String serialized = TextUtils.join(IP_DELIM, customDeviceList);
|
||||
sharedPreferences
|
||||
.edit()
|
||||
@@ -129,30 +148,38 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
.apply();
|
||||
}
|
||||
|
||||
private static ArrayList<String> deserializeIpList(String serialized) {
|
||||
ArrayList<String> ipList = new ArrayList<>();
|
||||
private static ArrayList<DeviceHost> deserializeIpList(String serialized) {
|
||||
ArrayList<DeviceHost> ipList = new ArrayList<>();
|
||||
|
||||
if (!serialized.isEmpty()) {
|
||||
Collections.addAll(ipList, serialized.split(IP_DELIM));
|
||||
for (String ip: serialized.split(IP_DELIM)) {
|
||||
DeviceHost deviceHost = DeviceHost.toDeviceHostOrNull(ip);
|
||||
// To prevent crashes when migrating if invalid hosts are present
|
||||
if (deviceHost != null) {
|
||||
ipList.add(deviceHost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ipList;
|
||||
}
|
||||
|
||||
public static ArrayList<String> getCustomDeviceList(SharedPreferences sharedPreferences) {
|
||||
public static ArrayList<DeviceHost> getCustomDeviceList(Context context) {
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String deviceListPrefs = sharedPreferences.getString(KEY_CUSTOM_DEVLIST_PREFERENCE, "");
|
||||
|
||||
return deserializeIpList(deviceListPrefs);
|
||||
ArrayList<DeviceHost> list = deserializeIpList(deviceListPrefs);
|
||||
list.sort(Comparator.comparing(DeviceHost::toString));
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCustomDeviceClicked(String customDevice) {
|
||||
public void onCustomDeviceClicked(DeviceHost customDevice) {
|
||||
editingDeviceAtPosition = customDeviceList.indexOf(customDevice);
|
||||
showEditTextDialog(customDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCustomDeviceDismissed(String customDevice) {
|
||||
public void onCustomDeviceDismissed(DeviceHost customDevice) {
|
||||
lastDeletedCustomDevice = new DeletedCustomDevice(customDevice, customDeviceList.indexOf(customDevice));
|
||||
customDeviceList.remove(lastDeletedCustomDevice.position);
|
||||
customDevicesAdapter.notifyItemRemoved(lastDeletedCustomDevice.position);
|
||||
@@ -190,19 +217,44 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
public void onPositiveButtonClicked() {
|
||||
if (addDeviceDialog.editText.getText() != null) {
|
||||
String deviceNameOrIP = addDeviceDialog.editText.getText().toString().trim();
|
||||
DeviceHost host = DeviceHost.toDeviceHostOrNull(deviceNameOrIP);
|
||||
|
||||
// don't add empty string (after trimming)
|
||||
if (!deviceNameOrIP.isEmpty() && !customDeviceList.contains(deviceNameOrIP)) {
|
||||
if (editingDeviceAtPosition >= 0) {
|
||||
customDeviceList.set(editingDeviceAtPosition, deviceNameOrIP);
|
||||
customDevicesAdapter.notifyItemChanged(editingDeviceAtPosition);
|
||||
} else {
|
||||
customDeviceList.add(deviceNameOrIP);
|
||||
customDevicesAdapter.notifyItemInserted(customDeviceList.size() - 1);
|
||||
}
|
||||
if (host != null) {
|
||||
if (!customDeviceList.stream().anyMatch(h -> h.toString().equals(host.toString()))) {
|
||||
if (editingDeviceAtPosition >= 0) {
|
||||
customDeviceList.set(editingDeviceAtPosition, host);
|
||||
customDevicesAdapter.notifyItemChanged(editingDeviceAtPosition);
|
||||
host.checkReachable(() -> {
|
||||
runOnUiThread(() -> customDevicesAdapter.notifyItemChanged(editingDeviceAtPosition));
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Find insertion position to ensure list remains sorted
|
||||
int pos = 0;
|
||||
while (customDeviceList.size() - 1 >= pos && customDeviceList.get(pos).toString().compareTo(host.toString()) < 0) {
|
||||
pos++;
|
||||
}
|
||||
final int position = pos;
|
||||
|
||||
saveList();
|
||||
showEmptyListMessageIfRequired();
|
||||
customDeviceList.add(position, host);
|
||||
customDevicesAdapter.notifyItemInserted(pos);
|
||||
host.checkReachable(() -> {
|
||||
runOnUiThread(() -> customDevicesAdapter.notifyItemChanged(position));
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
saveList();
|
||||
showEmptyListMessageIfRequired();
|
||||
}
|
||||
else {
|
||||
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_duplicate, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
else {
|
||||
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_invalid, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,10 +266,10 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
}
|
||||
|
||||
private static class DeletedCustomDevice {
|
||||
@NonNull String hostnameOrIP;
|
||||
@NonNull DeviceHost hostnameOrIP;
|
||||
int position;
|
||||
|
||||
DeletedCustomDevice(@NonNull String hostnameOrIP, int position) {
|
||||
DeletedCustomDevice(@NonNull DeviceHost hostnameOrIP, int position) {
|
||||
this.hostnameOrIP = hostnameOrIP;
|
||||
this.position = position;
|
||||
}
|
||||
@@ -228,4 +280,9 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
|
||||
super.onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isScrollable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@
|
||||
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -16,21 +17,25 @@ import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.kde.kdeconnect.DeviceHost;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.CustomDeviceItemBinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdapter.ViewHolder> {
|
||||
private ArrayList<String> customDevices;
|
||||
private ArrayList<DeviceHost> customDevices;
|
||||
private final Callback callback;
|
||||
private final Context context;
|
||||
|
||||
CustomDevicesAdapter(@NonNull Callback callback) {
|
||||
CustomDevicesAdapter(@NonNull Callback callback, Context context) {
|
||||
this.callback = callback;
|
||||
this.context = context;
|
||||
|
||||
customDevices = new ArrayList<>();
|
||||
}
|
||||
|
||||
void setCustomDevices(ArrayList<String> customDevices) {
|
||||
void setCustomDevices(ArrayList<DeviceHost> customDevices) {
|
||||
this.customDevices = customDevices;
|
||||
|
||||
notifyDataSetChanged();
|
||||
@@ -51,12 +56,13 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
|
||||
CustomDeviceItemBinding itemBinding =
|
||||
CustomDeviceItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
|
||||
return new ViewHolder(itemBinding);
|
||||
return new ViewHolder(itemBinding, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
holder.bind(customDevices.get(position));
|
||||
DeviceHost deviceHost = customDevices.get(position);
|
||||
holder.bind(deviceHost.toString(), deviceHost.getPing());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,15 +72,29 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
|
||||
private final CustomDeviceItemBinding itemBinding;
|
||||
private final Context context;
|
||||
|
||||
ViewHolder(@NonNull CustomDeviceItemBinding itemBinding) {
|
||||
ViewHolder(@NonNull CustomDeviceItemBinding itemBinding, Context context) {
|
||||
super(itemBinding.getRoot());
|
||||
this.itemBinding = itemBinding;
|
||||
itemBinding.deviceNameOrIP.setOnClickListener(v -> callback.onCustomDeviceClicked(customDevices.get(getAdapterPosition())));
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
void bind(String customDevice) {
|
||||
void bind(String customDevice, DeviceHost.PingResult pingResult) {
|
||||
itemBinding.deviceNameOrIP.setText(customDevice);
|
||||
if (pingResult != null) {
|
||||
if (pingResult.getLatency() != null) {
|
||||
String text = context.getString(R.string.ping_result, pingResult.getLatency());
|
||||
itemBinding.connectionStatus.setText(text);
|
||||
}
|
||||
else {
|
||||
itemBinding.connectionStatus.setText(R.string.ping_failed);
|
||||
}
|
||||
}
|
||||
else {
|
||||
itemBinding.connectionStatus.setText(R.string.ping_in_progress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,7 +164,7 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onCustomDeviceClicked(String customDevice);
|
||||
void onCustomDeviceDismissed(String customDevice);
|
||||
void onCustomDeviceClicked(DeviceHost customDevice);
|
||||
void onCustomDeviceDismissed(DeviceHost customDevice);
|
||||
}
|
||||
}
|
||||
|
@@ -44,6 +44,7 @@ import org.kde.kdeconnect.Plugins.Plugin
|
||||
import org.kde.kdeconnect.Plugins.PresenterPlugin.PresenterPlugin
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTheme
|
||||
import org.kde.kdeconnect.extensions.setupBottomPadding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityDeviceBinding
|
||||
import org.kde.kdeconnect_tp.databinding.ViewPairErrorBinding
|
||||
@@ -156,6 +157,11 @@ class DeviceFragment : Fragment() {
|
||||
return deviceBinding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
deviceBinding?.deviceView?.setupBottomPadding()
|
||||
}
|
||||
|
||||
private fun refreshDevicesAction() {
|
||||
BackgroundService.ForceRefreshConnections(requireContext())
|
||||
requireErrorBinding().errorMessageContainer.isRefreshing = true
|
||||
|
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.UserInterface.List
|
||||
|
||||
import android.R
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
|
||||
class SmallEntryItem : ListAdapter.Item {
|
||||
private val title: String
|
||||
private val clickListener: View.OnClickListener?
|
||||
|
||||
constructor(title: String, clickListener: View.OnClickListener?) {
|
||||
this.title = title
|
||||
this.clickListener = clickListener
|
||||
}
|
||||
|
||||
override fun inflateView(layoutInflater: LayoutInflater): View {
|
||||
val v = layoutInflater.inflate(R.layout.simple_list_item_1, null)
|
||||
val padding = (28 * layoutInflater.context.resources.displayMetrics.density).toInt()
|
||||
v.setPadding(padding, 0, padding, 0)
|
||||
|
||||
val titleView = v.findViewById<TextView>(R.id.text1)
|
||||
if (titleView != null) {
|
||||
titleView.text = title
|
||||
if (clickListener != null) {
|
||||
titleView.setOnClickListener(clickListener)
|
||||
val outValue = TypedValue()
|
||||
layoutInflater.context.theme.resolveAttribute(
|
||||
R.attr.selectableItemBackground,
|
||||
outValue,
|
||||
true
|
||||
)
|
||||
v.setBackgroundResource(outValue.resourceId)
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
}
|
@@ -32,6 +32,7 @@ import androidx.fragment.app.Fragment;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
|
||||
import org.kde.kdeconnect.Helpers.WindowHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem;
|
||||
@@ -118,6 +119,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
// Configure focus order for Accessibility, for touchpads, and for TV remotes
|
||||
// (allow focus of items in the device list)
|
||||
devicesListBinding.devicesList.setItemsCanFocus(true);
|
||||
WindowHelper.setupBottomPadding(devicesListBinding.devicesList);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -13,8 +13,8 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
@@ -24,12 +24,17 @@ import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.DeviceStats;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.base.BaseActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityPluginSettingsBinding;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import kotlin.LazyKt;
|
||||
|
||||
public class PluginSettingsActivity
|
||||
extends AppCompatActivity
|
||||
extends BaseActivity<ActivityPluginSettingsBinding>
|
||||
implements PluginPreference.PluginPreferenceCallback {
|
||||
|
||||
public static final String EXTRA_DEVICE_ID = "deviceId";
|
||||
@@ -38,12 +43,18 @@ public class PluginSettingsActivity
|
||||
//TODO: Save/restore state
|
||||
static private String deviceId; //Static because if we get here by using the back button in the action bar, the extra deviceId will not be set.
|
||||
|
||||
private final Lazy<ActivityPluginSettingsBinding> lazyBinding = LazyKt.lazy(() -> ActivityPluginSettingsBinding.inflate(getLayoutInflater()));
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected ActivityPluginSettingsBinding getBinding() {
|
||||
return lazyBinding.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_plugin_settings);
|
||||
|
||||
setSupportActionBar(findViewById(R.id.toolbar));
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
@@ -6,6 +6,7 @@
|
||||
package org.kde.kdeconnect.UserInterface
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@@ -33,6 +34,7 @@ import org.kde.kdeconnect.Helpers.DeviceHelper.filterName
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper.getDeviceName
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil.applyTheme
|
||||
import org.kde.kdeconnect.extensions.setupBottomPadding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
@@ -61,6 +63,11 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||
preferenceScreen = screen
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
listView.setupBottomPadding()
|
||||
}
|
||||
|
||||
private fun deviceNamePref(context: Context) = EditTextPreference(context).apply {
|
||||
key = DeviceHelper.KEY_DEVICE_NAME_PREFERENCE
|
||||
isSelectable = true
|
||||
@@ -146,21 +153,43 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||
setTitle(R.string.trusted_networks)
|
||||
setSummary(R.string.trusted_networks_desc)
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
startActivity(Intent(context, TrustedNetworksActivity::class.java))
|
||||
startActivityForResult(Intent(context, TrustedNetworksActivity::class.java), REQUEST_REFRESH_NETWORKS)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private val REQUEST_REFRESH_DEVICES_BY_IP = 1
|
||||
private val REQUEST_REFRESH_NETWORKS = 2
|
||||
|
||||
private lateinit var devicesByIpPref : Preference
|
||||
|
||||
/** Opens activity to configure device by IP when clicked */
|
||||
private fun devicesByIpPref(context: Context) = Preference(context).apply {
|
||||
devicesByIpPref = this
|
||||
isPersistent = false
|
||||
setTitle(R.string.custom_device_list)
|
||||
updateDevicesByIpSummary()
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
startActivity(Intent(context, CustomDevicesActivity::class.java))
|
||||
startActivityForResult(Intent(context, CustomDevicesActivity::class.java), REQUEST_REFRESH_DEVICES_BY_IP)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when (requestCode) {
|
||||
REQUEST_REFRESH_DEVICES_BY_IP -> updateDevicesByIpSummary()
|
||||
REQUEST_REFRESH_NETWORKS -> BackgroundService.instance?.onNetworkChange(null)
|
||||
else -> super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDevicesByIpSummary() {
|
||||
devicesByIpPref.setSummary(getString(
|
||||
R.string.custom_devices_settings_summary,
|
||||
CustomDevicesActivity.getCustomDeviceList(context).size
|
||||
))
|
||||
}
|
||||
|
||||
private fun udpBroadcastPref(context: Context) = SwitchPreference(context).apply {
|
||||
setDefaultValue(true)
|
||||
key = KEY_UDP_BROADCAST_ENABLED
|
||||
|
@@ -12,13 +12,14 @@ import android.view.View
|
||||
import android.widget.*
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import org.kde.kdeconnect.Helpers.TrustedNetworkHelper
|
||||
import org.kde.kdeconnect.base.BaseActivity
|
||||
import org.kde.kdeconnect.extensions.viewBinding
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.TrustedNetworkListBinding
|
||||
|
||||
class TrustedNetworksActivity : AppCompatActivity() {
|
||||
lateinit var binding: TrustedNetworkListBinding
|
||||
class TrustedNetworksActivity : BaseActivity<TrustedNetworkListBinding>() {
|
||||
override val binding: TrustedNetworkListBinding by viewBinding(TrustedNetworkListBinding::inflate)
|
||||
private val trustedNetworks: MutableList<String> = mutableListOf()
|
||||
|
||||
private val trustedNetworksView: ListView
|
||||
@@ -39,9 +40,6 @@ class TrustedNetworksActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = TrustedNetworkListBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setSupportActionBar(binding.toolbarLayout.toolbar)
|
||||
supportActionBar!!.apply {
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
|
45
src/org/kde/kdeconnect/base/BaseActivity.kt
Normal file
45
src/org/kde/kdeconnect/base/BaseActivity.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Mash Kyrielight <fiepi@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.base
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import org.kde.kdeconnect.extensions.getSafeDrawInsets
|
||||
import org.kde.kdeconnect.extensions.setOnApplyWindowInsetsListenerCompat
|
||||
|
||||
abstract class BaseActivity<VB: ViewBinding> : AppCompatActivity() {
|
||||
|
||||
protected abstract val binding: VB
|
||||
|
||||
open val isScrollable = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
binding.root.setOnApplyWindowInsetsListenerCompat { view, insets ->
|
||||
onWindowInsetsChanged(view, insets)
|
||||
insets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open fun onWindowInsetsChanged(view: View, insets: WindowInsetsCompat) {
|
||||
val safeDrawInsets = insets.getSafeDrawInsets()
|
||||
view.updateLayoutParams<MarginLayoutParams> {
|
||||
bottomMargin = if (isScrollable) 0 else safeDrawInsets.bottom
|
||||
leftMargin = safeDrawInsets.left
|
||||
rightMargin = safeDrawInsets.right
|
||||
}
|
||||
}
|
||||
|
||||
}
|
15
src/org/kde/kdeconnect/extensions/ViewBinding.kt
Normal file
15
src/org/kde/kdeconnect/extensions/ViewBinding.kt
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Mash Kyrielight <fiepi@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.extensions
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.viewbinding.ViewBinding
|
||||
|
||||
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) =
|
||||
lazy(LazyThreadSafetyMode.NONE) {
|
||||
bindingInflater.invoke(layoutInflater)
|
||||
}
|
63
src/org/kde/kdeconnect/extensions/Window.kt
Normal file
63
src/org/kde/kdeconnect/extensions/Window.kt
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Mash Kyrielight <fiepi@live.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.extensions
|
||||
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
|
||||
fun View.setOnApplyWindowInsetsListenerCompat(listener: (v: View, insets: WindowInsetsCompat) -> WindowInsetsCompat) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(this, listener)
|
||||
}
|
||||
|
||||
fun WindowInsetsCompat.getSafeDrawInsets(): Insets {
|
||||
return getInsets(
|
||||
WindowInsetsCompat.Type.systemBars()
|
||||
or WindowInsetsCompat.Type.displayCutout()
|
||||
or WindowInsetsCompat.Type.ime()
|
||||
)
|
||||
}
|
||||
|
||||
fun View.setupBottomPadding() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
return
|
||||
}
|
||||
val originalBottomPadding = paddingBottom
|
||||
setOnApplyWindowInsetsListenerCompat { _, insets ->
|
||||
val safeInsets = insets.getSafeDrawInsets()
|
||||
updatePadding(bottom = originalBottomPadding + safeInsets.bottom)
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
fun View.setupBottomMargin() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
return
|
||||
}
|
||||
val originalBottomMargin = (layoutParams as MarginLayoutParams).bottomMargin
|
||||
setOnApplyWindowInsetsListenerCompat { _, insets ->
|
||||
val safeInsets = insets.getSafeDrawInsets()
|
||||
updateLayoutParams<MarginLayoutParams> {
|
||||
bottomMargin = originalBottomMargin + safeInsets.bottom
|
||||
}
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
fun Modifier.safeDrawPadding(): Modifier {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
safeDrawingPadding()
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user