Compare commits

...
52 Commits
Author SHA1 Message Date
darken 18a7311089 Release: 2.11.0-rc0 2023-09-11 22:45:47 +02:00
darken 9f49d62f12 Clean up store description and remove WearOS sentences. 2023-09-11 21:09:02 +02:00
darken 0c3c4e7ea1 Update translations 2023-09-11 21:09:02 +02:00
darken 7cdf653842 Remove WearOS part from app description, it's no longer on Google Play. 2023-09-11 21:09:02 +02:00
darken 32d99eada8 Prevent app from crashing if no app to view urls is installed 2023-09-11 20:57:15 +02:00
darken 9e4adb16c6 Hide install, not useful without 3rd party bug tracker 2023-09-11 20:33:06 +02:00
darken 4b5638e4f0 Hide bug reporting setting (but lets keep the setting ID because if we re-add we might use the setting state again). 2023-09-11 20:32:53 +02:00
darken d8fb9cdd30 Remove bugsnag 2023-09-11 20:32:53 +02:00
Matthias Urhahn 3920489dcd Extra onboarding step (#158)
* wip

* Add extra onboarding step
2023-09-11 20:32:41 +02:00
Matthias Urhahn 09d013588d Update PRIVACY_POLICY.md 2023-09-11 01:33:38 +02:00
darken d8bd03c65d Remove WearOS build from Google Play pipeline. 2023-07-16 01:05:04 +02:00
darken 8b2c6b9498 Release: 2.10.0-rc2 2023-07-07 10:13:48 +02:00
darken 0f63d3a0d4 Revert "Don't bump WearOS, not needed due to new track system?"
This reverts commit a321bb733c.
2023-07-07 10:12:56 +02:00
darken 672eb333b0 Update fastlane 2023-07-07 10:12:24 +02:00
darken de609b4d05 Release: 2.10.0-rc1 2023-07-07 09:05:38 +02:00
darken 7e6e9eee36 Try WearOS first as it is more likely to fail. 2023-07-07 09:04:34 +02:00
darken a321bb733c Don't bump WearOS, not needed due to new track system? 2023-07-07 09:04:34 +02:00
darken 490369a3e1 Adjust CI to build and upload to WearOS beta/production tracks 2023-07-07 09:04:34 +02:00
darken a2cce044ef Release: 2.10.0-rc0 2023-07-05 09:16:53 +02:00
darken 8884bb88c7 Update translations 2023-07-05 09:15:44 +02:00
darken 313b0e9131 Include changelog in changelog. 2023-07-05 09:15:44 +02:00
darken 47048cf7e7 Update translations 2023-07-05 09:15:44 +02:00
darken ef1c4615aa Add reaction option: Popup on new connection 2023-07-05 08:21:37 +02:00
Matthias Urhahn c9587b2bf0 Update translations (#147)
* Update translations

* Update translations
2023-07-04 12:41:14 +02:00
Sean Singh 669fa2fd22 Support edge-to-edge rendering on Android Q+ (#146)
* Set nav bars transparent in themes.xml files

* App renders edge-to-edge; need to handle scroll overlaps

* Add fake padding

* Begin version separating

* Edge-to-edge working everywhere except main page

* Finalized

* Cleanup Finalized

* Cleanup Finalized

* Removed redundant values-v29 & optimize imports

* Consolidated insets to Fragment2

* Consolidated insets to Fragment2 (file wasn't staged)
2023-07-04 12:17:44 +02:00
darken fb8ec72825 Release: 2.9.3-rc0 2023-06-27 22:07:22 +02:00
Matthias Urhahn d1687f574f Merge pull request #145 from d4rken-org/bump_workmanager
Fix background execution errors.
2023-06-27 22:04:28 +02:00
darken 72f47bec1f Fix background execution errors.
Bump workmanager `2.7.1` -> `2.8.1` to fix

```java
android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service eu.darken.capod/androidx.work.impl.foreground.SystemForegroundService
```
2023-06-27 21:55:58 +02:00
Matthias Urhahn 20e8aecf83 Merge pull request #144 from d4rken-org/gplay_hates_splash
Fix "Missing app icon in splash screen"
2023-06-27 21:49:50 +02:00
Matthias Urhahn ee41474670 Merge pull request #143 from d4rken-org/gplay_wear_policies_fml
Fix "Missing show time"
2023-06-27 21:49:37 +02:00
darken ac29e28eda Fix "Missing app icon in splash screen"
> Your app does not show a 48x48dp app icon on a black background during app startup. For more information, see Branded launch.
2023-06-27 21:49:05 +02:00
darken 58b96cbdee Fix "Missing show time"
> Your app does not display the time of day clearly at the top of the app home screen and any ongoing activity screens. We recommend that you display the time of day at the top of all activities except dialog and confirmation screens. For more information, see Show the time.
2023-06-27 21:12:16 +02:00
darken 0c1fc15251 Update translations 2023-06-27 20:28:31 +02:00
darken 7b8e0cc952 Update translations 2023-06-27 20:28:31 +02:00
darken c8aa591feb Update AGP 2023-06-27 20:28:21 +02:00
darken afa4d6e807 Release: 2.9.2-rc0 2023-05-16 09:23:59 +02:00
darken e6b7eda9e7 Update store translations 2023-05-16 09:23:09 +02:00
darken bbc4e05c8f Update translations 2023-05-16 09:23:09 +02:00
darken 50bccbaa2e Update Crowdin tooling 2023-05-16 09:23:09 +02:00
Matthias Urhahn d71e83a2af Merge pull request #132 from d4rken-org/tweak_widget_layout
Tweak widget layout
2023-05-16 09:14:30 +02:00
Matthias Urhahn 92a67702b7 Merge pull request #131 from d4rken-org/gradle_bump
Gradle bump
2023-05-16 09:04:46 +02:00
darken 2f9185efa7 Tweak dual pod widget.
Less padding to reduce cutoff chances and put the label at the type bottom as it's the least important info.
2023-05-16 09:00:58 +02:00
darken a10216520b Fix resource reference 2023-05-16 08:59:54 +02:00
darken d5354e5156 Fix resource reference 2023-05-16 08:55:35 +02:00
darken a8f9b1f732 Use non final resource ids 2023-05-16 08:07:48 +02:00
darken 08a796e621 Fix wear namespace 2023-05-16 08:05:59 +02:00
darken 15f3a68f8e Use non transitive R classes to improve performance 2023-05-16 07:58:28 +02:00
darken e0bc6dafa0 Bump gradle 2023-05-16 07:46:13 +02:00
darken 430c1236d7 Fix typo 2023-05-07 20:15:41 +02:00
darken 3ac357161a Improve strings based on translator feedback 2023-05-07 20:15:41 +02:00
darken 3c5f1bf21f CI: Remove unused base flavor, all our modules are flavored :) 2023-04-07 11:00:44 +02:00
darken 5544cd3770 CI: Update action dependencies, improve naming and use more specific build commands 2023-04-07 11:00:44 +02:00
189 changed files with 1818 additions and 928 deletions
+18 -17
View File
@@ -7,46 +7,47 @@ on:
branches: [ main ]
jobs:
build-all:
name: Assemble all variants
build-modules:
name: Build apps
strategy:
fail-fast: false
matrix:
flavor: [ Foss,Gplay ]
variant: [ Debug ]
module: [ app,app-wear ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build (Flavor ${{ matrix.flavor }}, Variant ${{ matrix.variant }})
run: ./gradlew assemble${{ matrix.flavor }}${{ matrix.variant }}
- name: Build module
run: ./gradlew ${{ matrix.module }}:assemble${{ matrix.flavor }}${{ matrix.variant }}
test-all:
name: Run all tests
test-modules:
name: Unit tests
strategy:
fail-fast: false
matrix:
flavor: [ Foss,Gplay ]
variant: [ Release ]
flavor: [ testFoss,testGplay ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run tests (Flavor ${{ matrix.flavor }}, Variant ${{ matrix.variant }})
run: ./gradlew test${{ matrix.flavor }}${{ matrix.variant }}
- name: Test modules
run: ./gradlew ${{ matrix.flavor }}${{ matrix.variant }}UnitTest
+19 -7
View File
@@ -38,7 +38,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: 11
java-version: 17
- name: Assemble beta APK
if: contains(steps.tagger.outputs.tag, '-beta')
@@ -48,7 +48,6 @@ jobs:
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
- name: Assemble production APK
if: "!contains(steps.tagger.outputs.tag, '-beta')"
@@ -58,7 +57,6 @@ jobs:
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
- name: Create pre-release
if: contains(steps.tagger.outputs.tag, '-beta')
@@ -130,7 +128,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: 11
java-version: 17
- name: Set up ruby env
uses: ruby/setup-ruby@v1
@@ -138,6 +136,14 @@ jobs:
ruby-version: 2.7.6
bundler-cache: true
# - name: Assemble WearOS beta and upload to Google Play
# if: contains(steps.tagger.outputs.tag, '-beta')
# run: bundle exec fastlane beta_wearos
# env:
# STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
# KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
# KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- name: Assemble beta and upload to Google Play
if: contains(steps.tagger.outputs.tag, '-beta')
run: bundle exec fastlane beta
@@ -145,7 +151,14 @@ jobs:
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
# - name: Assemble WearOS production and upload to Google Play
# if: "!contains(steps.tagger.outputs.tag, '-beta')"
# run: bundle exec fastlane production_wearos
# env:
# STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
# KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
# KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
- name: Assemble production and upload to Google Play
if: "!contains(steps.tagger.outputs.tag, '-beta')"
@@ -153,5 +166,4 @@ jobs:
env:
STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
+42 -42
View File
@@ -1,27 +1,27 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.5)
CFPropertyList (3.0.6)
rexml
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.610.0)
aws-sdk-core (3.131.3)
aws-partitions (1.784.0)
aws-sdk-core (3.177.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.58.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (1.70.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-s3 (1.128.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.1)
aws-sigv4 (~> 1.6)
aws-sigv4 (1.6.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
@@ -30,14 +30,14 @@ GEM
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
digest-crc (0.6.4)
digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
dotenv (2.8.1)
emoji_regex (3.2.3)
excon (0.92.4)
faraday (1.10.0)
excon (0.100.0)
faraday (1.10.3)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
@@ -65,8 +65,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.208.0)
fastimage (2.2.7)
fastlane (2.213.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -90,7 +90,7 @@ GEM
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
@@ -106,9 +106,9 @@ GEM
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.25.0)
google-apis-core (>= 0.7, < 2.a)
google-apis-core (0.7.0)
google-apis-androidpublisher_v3 (0.45.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
@@ -117,27 +117,27 @@ GEM
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.13.0)
google-apis-core (>= 0.7, < 2.a)
google-apis-playcustomapp_v1 (0.10.0)
google-apis-core (>= 0.7, < 2.a)
google-apis-storage_v1 (0.18.0)
google-apis-core (>= 0.7, < 2.a)
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.19.0)
google-apis-core (>= 0.9.0, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.37.0)
google-cloud-errors (1.3.1)
google-cloud-storage (1.44.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.19.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.2.0)
googleauth (1.6.0)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
@@ -148,20 +148,20 @@ GEM
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.1)
json (2.6.2)
jwt (2.4.1)
jmespath (1.6.2)
json (2.6.3)
jwt (2.7.1)
memoist (0.16.2)
mini_magick (4.11.0)
mini_magick (4.12.0)
mini_mime (1.1.2)
multi_json (1.15.0)
multipart-post (2.0.0)
multipart-post (2.3.0)
nanaimo (0.3.0)
naturally (2.2.1)
optparse (0.1.1)
os (1.1.4)
plist (3.6.0)
public_suffix (4.0.7)
plist (3.7.0)
public_suffix (5.0.1)
rake (13.0.6)
representable (3.2.0)
declarative (< 0.1.0)
@@ -178,7 +178,7 @@ GEM
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)
simctl (1.6.10)
CFPropertyList
naturally
terminal-notifier (2.0.0)
@@ -194,7 +194,7 @@ GEM
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
webrick (1.8.1)
word_wrap (1.0.0)
xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0)
+28 -8
View File
@@ -20,23 +20,43 @@ On Android 12+ the newer and more fine grained `BLUETOOTH_SCAN` permission is us
Bluetooth Low Energy is a technology that devices like AirPods use to communicate their status to nearby devices.
CAPod requests location permissions because these permissions are required to work with Bluetooth Low Energy data.
This is a privacy measure on Android's side because you could determine someones location by scanning for Bluetooth devices:
If you know the physical location of a Bluetooth device (e.g. AirTags) you could use Bluetooth data to calculate your position.
This is a privacy measure on Android's side because you could determine someones location by scanning for Bluetooth
devices:
If you know the physical location of a Bluetooth device (e.g. AirTags) you could use Bluetooth data to calculate your
position.
### Location access in the background
CAPod uses the "location access in the background" permission (`ACCESS_BACKGROUND_LOCATION`) on Android 11 and older to receive Bluetooth Low Energy data while the app is in the background. This permission enables the "Show popup" and "Autoconnect" features and allows CAPod to react to nearby devices whil the app is closed.
CAPod uses the "location access in the background" permission (`ACCESS_BACKGROUND_LOCATION`) on Android 11 and older to
receive Bluetooth Low Energy data while the app is in the background. This permission enables the "Show popup" and "
Autoconnect" features and allows CAPod to react to nearby devices whil the app is closed.
## Automatic crash reports
## Automatic error reports
Anonymous device information may be collected in the event of an app crash or error.
*This was removed in v2.11.0+*
To do this the app uses the service "Bugsnag":
If an error occurs, an automated report may be sent to help me fix the issue.
This is optional and you can opt out of this in the settings.
Error reports are collected using "Bugsnag":
https://www.bugsnag.com/
Bugsnag's privacy policy can be found here:
https://docs.bugsnag.com/legal/privacy-policy/
Crash reports may contain device and app related information, e.g. your phone model, Android version and app version.
Error reports contain device and app information related to the error that occured.
Additional information about the error context may also be included, e.g. what this app did shortly before the error.
You can disable automatic reports in the app's settings.
Additional details about the type of data that is collected by the error tracking SDK can be found here:
https://docs.bugsnag.com/platforms/android/playstore-privacy/
Error reports are pseudonymous. Unless you tell me your install-ID, I don't know that an error report came from you.
Error reports are automatically deleted after 90 days.
## Debug logs
The app has a debug log feature that can be used to assist troubleshooting efforts. This feature creates a log file that contains verbose output of what the app is doing.
It is manually triggered by the user through an option in the app settings. The recorded log file can be shared through compatible apps (e.g. your email app) using the system's share dialog. As this log file may contain sensitive information (e.g. details about files or installed applications) it should only be shared with trusted parties.
+1 -1
View File
@@ -1 +1 @@
2.9.1-rc0 20901000
2.11.0-rc0 21100000
+4 -10
View File
@@ -8,6 +8,7 @@ apply(plugin = "dagger.hilt.android.plugin")
android {
compileSdk = ProjectConfig.compileSdk
namespace = "${ProjectConfig.packageName}.common"
defaultConfig {
minSdk = ProjectConfig.minSdk
@@ -21,10 +22,6 @@ android {
buildConfigField("String", "APPLICATION_ID", "\"${ProjectConfig.packageName}\"")
buildConfigField("String", "GITSHA", "\"${lastCommitHash()}\"")
buildConfigField("String", "BUILDTIME", "\"${buildTime()}\"")
manifestPlaceholders["bugsnagApiKey"] = getBugSnagApiKey(
File(System.getProperty("user.home"), ".appconfig/${ProjectConfig.packageName}/bugsnag.properties")
) ?: "bugsnag_apikey_placeholder"
}
buildFeatures {
@@ -33,12 +30,12 @@ android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
freeCompilerArgs = freeCompilerArgs + listOf(
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
@@ -99,7 +96,4 @@ dependencies {
addTesting()
"gplayImplementation"("com.bugsnag:bugsnag-android:5.9.2")
"gplayImplementation"("com.getkeepsafe.relinker:relinker:1.4.3")
}
+2 -4
View File
@@ -1,10 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
<application>
<meta-data
android:name="com.bugsnag.android.API_KEY"
android:value="${bugsnagApiKey}" />
</application>
</manifest>
@@ -1,58 +0,0 @@
package eu.darken.capod.debug.autoreport
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import com.bugsnag.android.Event
import com.bugsnag.android.OnErrorCallback
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.BuildConfigWrap
import eu.darken.capod.common.debug.DebugSettings
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
import eu.darken.capod.common.debug.logging.asLog
import eu.darken.capod.common.debug.logging.log
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BugsnagErrorHandler @Inject constructor(
@ApplicationContext private val context: Context,
private val bugsnagLogger: BugsnagLogger,
private val debugSettings: DebugSettings,
) : OnErrorCallback {
override fun onError(event: Event): Boolean {
bugsnagLogger.injectLog(event)
TAB_APP.also { tab ->
event.addMetadata(tab, "gitSha", BuildConfigWrap.GIT_SHA)
event.addMetadata(tab, "buildTime", BuildConfigWrap.BUILDTIME)
context.tryFormattedSignature()?.let { event.addMetadata(tab, "signatures", it) }
}
return debugSettings.isAutoReportingEnabled.value && !eu.darken.capod.common.BuildConfigWrap.DEBUG
}
companion object {
private const val TAB_APP = "app"
@Suppress("DEPRECATION")
@SuppressLint("PackageManagerGetSignatures")
fun Context.tryFormattedSignature(): String? = try {
packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures?.let { sigs ->
val sb = StringBuilder("[")
for (i in sigs.indices) {
sb.append(sigs[i].hashCode())
if (i + 1 != sigs.size) sb.append(", ")
}
sb.append("]")
sb.toString()
}
} catch (e: Exception) {
log(WARN) { e.asLog() }
null
}
}
}
@@ -1,47 +0,0 @@
package eu.darken.capod.debug.autoreport
import com.bugsnag.android.Event
import eu.darken.capod.common.debug.logging.Logging
import eu.darken.capod.common.debug.logging.asLog
import java.lang.String.format
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BugsnagLogger @Inject constructor() : Logging.Logger {
// Adding one to the initial size accounts for the add before remove.
private val buffer: Deque<String> = ArrayDeque(BUFFER_SIZE + 1)
override fun log(priority: Logging.Priority, tag: String, message: String, metaData: Map<String, Any>?) {
val line = "${System.currentTimeMillis()} ${priority.toLabel()}/$tag: $message"
synchronized(buffer) {
buffer.addLast(line)
if (buffer.size > BUFFER_SIZE) {
buffer.removeFirst()
}
}
}
fun injectLog(event: Event) {
synchronized(buffer) {
var i = 100
buffer.forEach { event.addMetadata("Log", format(Locale.ROOT, "%03d", i++), it) }
event.addMetadata("Log", format(Locale.ROOT, "%03d", i), event.originalError?.asLog())
}
}
companion object {
private const val BUFFER_SIZE = 200
private fun Logging.Priority.toLabel(): String = when (this) {
Logging.Priority.VERBOSE -> "V"
Logging.Priority.DEBUG -> "D"
Logging.Priority.INFO -> "I"
Logging.Priority.WARN -> "W"
Logging.Priority.ERROR -> "E"
Logging.Priority.ASSERT -> "WTF"
}
}
}
@@ -2,20 +2,15 @@ package eu.darken.capod.debug.autoreport
import android.app.Application
import android.content.Context
import com.bugsnag.android.Bugsnag
import com.bugsnag.android.Configuration
import com.getkeepsafe.relinker.ReLinker
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.BuildConfigWrap
import eu.darken.capod.common.InstallId
import eu.darken.capod.common.debug.Bugs
import eu.darken.capod.common.debug.DebugSettings
import eu.darken.capod.common.debug.autoreport.AutomaticBugReporter
import eu.darken.capod.common.debug.logging.Logging
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Singleton
@Singleton
@@ -23,9 +18,6 @@ class GplayAutoReporting @Inject constructor(
@ApplicationContext private val context: Context,
private val debugSettings: DebugSettings,
private val installId: InstallId,
private val bugsnagLogger: Provider<BugsnagLogger>,
private val bugsnagErrorHandler: Provider<BugsnagErrorHandler>,
private val nopBugsnagErrorHandler: Provider<NOPBugsnagErrorHandler>,
) : AutomaticBugReporter {
override fun setup(application: Application) {
@@ -34,35 +26,13 @@ class GplayAutoReporting @Inject constructor(
if (!isEnabled) return
ReLinker
.log { message -> log(TAG) { "ReLinker: $message" } }
.loadLibrary(application, "bugsnag-plugin-android-anr")
// Currently no 3rd party bug tracking
try {
val bugsnagConfig = Configuration.load(context).apply {
if (debugSettings.isAutoReportingEnabled.value) {
Logging.install(bugsnagLogger.get())
setUser(installId.id, null, null)
autoTrackSessions = true
addOnError(bugsnagErrorHandler.get())
addMetadata("App", "buildFlavor", BuildConfigWrap.FLAVOR)
log(TAG) { "Bugsnag setup done!" }
} else {
autoTrackSessions = false
addOnError(nopBugsnagErrorHandler.get())
log(TAG) { "Installing Bugsnag NOP error handler due to user opt-out!" }
}
}
Bugsnag.start(context, bugsnagConfig)
Bugs.reporter = this
} catch (e: IllegalStateException) {
log(TAG) { "Bugsnag API Key not configured." }
}
Bugs.reporter = this
}
override fun notify(throwable: Throwable) {
Bugsnag.notify(throwable)
log(TAG, WARN) { "notify($throwable)" }
}
companion object {
@@ -1,19 +0,0 @@
package eu.darken.capod.debug.autoreport
import com.bugsnag.android.Event
import com.bugsnag.android.OnErrorCallback
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
import eu.darken.capod.common.debug.logging.asLog
import eu.darken.capod.common.debug.logging.log
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NOPBugsnagErrorHandler @Inject constructor() : OnErrorCallback {
override fun onError(event: Event): Boolean {
log(WARN) { "Error, but skipping bugsnag due to user opt-out: ${event.originalError?.asLog()}" }
return false
}
}
@@ -0,0 +1,15 @@
package eu.darken.capod.common.bluetooth
import android.bluetooth.BluetoothDevice
import java.time.Instant
data class BluetoothDevice2(
internal val internal: BluetoothDevice,
val seenFirstAt: Instant,
) {
val address: String
get() = internal.address
val name: String?
get() = internal.name
}
@@ -1,6 +1,10 @@
package eu.darken.capod.common.bluetooth
import android.bluetooth.*
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothHeadset
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.bluetooth.le.BluetoothLeScanner
import android.content.BroadcastReceiver
import android.content.Context
@@ -12,14 +16,24 @@ import android.os.ParcelUuid
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.Bugs
import eu.darken.capod.common.debug.logging.Logging.Priority.*
import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.pods.core.apple.protocol.ContinuityProtocol
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.IOException
import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
@@ -74,7 +88,7 @@ class BluetoothManager2 @Inject constructor(
}
override fun onServiceDisconnected(profile: Int) {
log(TAG, WARN) { "onServiceDisconnected(profile=$profile" }
log(TAG, WARN) { "onServiceDisconnected(profile=$profile)" }
close(IOException("BluetoothProfile service disconnected (profile=$profile)"))
}
@@ -128,6 +142,7 @@ class BluetoothManager2 @Inject constructor(
log(TAG) { "Adding $device to current devices $currentDevices" }
trySend(currentDevices.plus(device))
}
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
log(TAG) { "Removing $device from current devices $currentDevices" }
trySend(currentDevices.minus(device))
@@ -145,23 +160,54 @@ class BluetoothManager2 @Inject constructor(
}
}
private val seenDevicesLock = Mutex()
private val seenDevicesCache = mutableMapOf<String, Instant>()
fun connectedDevices(
featureFilter: Set<ParcelUuid> = ContinuityProtocol.BLE_FEATURE_UUIDS
): Flow<List<BluetoothDevice>> = isBluetoothEnabled
): Flow<List<BluetoothDevice2>> = isBluetoothEnabled
.flatMapLatest { monitorDevicesForProfile(BluetoothProfile.HEADSET) }
.map { devices ->
devices.filter { device ->
featureFilter.any { feature ->
device.hasFeature(feature)
}
val currentAddresses = devices.map { it.address }
seenDevicesLock.withLock {
val cleanedCache = seenDevicesCache.filterKeys { currentAddresses.contains(it) }
seenDevicesCache.clear()
seenDevicesCache.putAll(cleanedCache)
}
devices
.filter { device -> featureFilter.any { feature -> device.hasFeature(feature) } }
.map { device ->
BluetoothDevice2(
internal = device,
seenFirstAt = seenDevicesLock.withLock {
seenDevicesCache[device.address] ?: Instant.now().also {
seenDevicesCache[device.address] = it
}
}
)
}
}
fun bondedDevices(): Flow<Set<BluetoothDevice>> = flow {
emit(adapter?.bondedDevices ?: throw IllegalStateException("Bluetooth adapter unavailable"))
fun bondedDevices(): Flow<Set<BluetoothDevice2>> = flow {
val rawDevices = adapter?.bondedDevices ?: throw IllegalStateException("Bluetooth adapter unavailable")
val wrappedDevices = rawDevices.map { device ->
BluetoothDevice2(
internal = device,
seenFirstAt = seenDevicesLock.withLock {
seenDevicesCache[device.address] ?: Instant.now().also {
seenDevicesCache[device.address] = it
}
}
)
}.toSet()
emit(wrappedDevices)
}
suspend fun nudgeConnection(device: BluetoothDevice): Boolean = getBluetoothProfile().map { bluetoothProfile ->
suspend fun nudgeConnection(device: BluetoothDevice2): Boolean = getBluetoothProfile().map { bluetoothProfile ->
try {
log(TAG) { "Nudging Android connection to $device" }
@@ -169,7 +215,7 @@ class BluetoothManager2 @Inject constructor(
"connect", BluetoothDevice::class.java
).apply { isAccessible = true }
connectMethod.invoke(bluetoothProfile.proxy, device)
connectMethod.invoke(bluetoothProfile.proxy, device.internal)
log(TAG) { "Nudged connection to $device" }
true
@@ -3,6 +3,7 @@ package eu.darken.capod.common.uix
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.lifecycle.LiveData
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
@@ -15,6 +16,8 @@ abstract class Activity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
log(tag, VERBOSE) { "onCreate(savedInstanceState=$savedInstanceState)" }
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows( window, false )
}
override fun onResume() {
@@ -7,6 +7,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
@@ -40,6 +42,17 @@ abstract class Fragment2(@LayoutRes val layoutRes: Int?) : Fragment(layoutRes ?:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
log(tag, VERBOSE) { "onViewCreated(view=$view, savedInstanceState=$savedInstanceState)" }
super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val systemWindowInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(
v.paddingLeft,
systemWindowInsets.top,
v.paddingRight,
0
)
insets
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -8,6 +8,7 @@ import eu.darken.capod.common.error.ErrorEventSource
import eu.darken.capod.common.flow.setupCommonEventHandlers
import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.navigation.NavEventSource
import eu.darken.capod.common.navigation.navVia
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
@@ -31,4 +32,7 @@ abstract class ViewModel3(
.setupCommonEventHandlers(TAG) { "launchInViewModel()" }
.launchIn(vmScope)
fun NavDirections.navigate() {
navVia(navEvents)
}
}
@@ -40,6 +40,8 @@ class GeneralSettings @Inject constructor(
val isOffloadedBatchingDisabled = preferences.createFlowPreference("core.compat.offloaded.batching.disabled", false)
val useIndirectScanResultCallback = preferences.createFlowPreference("core.compat.indirectcallback.enabled", false)
val isOnboardingDone = preferences.createFlowPreference("core.onboarding.done", false)
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
monitorMode,
scannerMode,
@@ -47,6 +47,11 @@ class ReactionSettings @Inject constructor(
false
)
val showPopUpOnConnection = preferences.createFlowPreference(
"reaction.popup.connected",
false
)
val onePodMode = preferences.createFlowPreference(
"reaction.onepod.enabled",
false
@@ -58,6 +63,7 @@ class ReactionSettings @Inject constructor(
autoConnect,
autoConnectCondition,
showPopUpOnCaseOpen,
showPopUpOnConnection,
onePodMode,
)
}
@@ -1,15 +1,26 @@
package eu.darken.capod.reaction.core.popup
import eu.darken.capod.common.debug.logging.Logging.Priority.*
import eu.darken.capod.common.bluetooth.BluetoothManager2
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.flow.setupCommonEventHandlers
import eu.darken.capod.common.flow.withPrevious
import eu.darken.capod.main.core.GeneralSettings
import eu.darken.capod.monitor.core.PodMonitor
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.reaction.core.ReactionSettings
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.merge
import java.time.Duration
import java.time.Instant
import javax.inject.Inject
@@ -19,11 +30,13 @@ import javax.inject.Singleton
class PopUpReaction @Inject constructor(
private val podMonitor: PodMonitor,
private val reactionSettings: ReactionSettings,
private val generalSettings: GeneralSettings,
private val bluetoothManager: BluetoothManager2,
) {
private val coolDowns = mutableMapOf<PodDevice.Id, Instant>()
private val caseCoolDowns = mutableMapOf<PodDevice.Id, Instant>()
fun monitor(): Flow<Event> = reactionSettings.showPopUpOnCaseOpen.flow
private fun monitorCase(): Flow<Event> = reactionSettings.showPopUpOnCaseOpen.flow
.flatMapLatest { isEnabled ->
if (isEnabled) {
podMonitor.mainDevice.distinctUntilChangedBy { it?.rawDataHex }
@@ -32,7 +45,7 @@ class PopUpReaction @Inject constructor(
}
}
.withPrevious()
.setupCommonEventHandlers(TAG) { "monitor" }
.setupCommonEventHandlers(TAG) { "popUpCase" }
.mapNotNull { (previous, current) ->
if (previous !is DualApplePods? || current !is DualApplePods) {
return@mapNotNull null
@@ -54,35 +67,37 @@ class PopUpReaction @Inject constructor(
}
log(TAG) { "Case lid status changed for monitored device." }
tryPopWindow(current)
throttleCasePopUps(current)
}
private suspend fun tryPopWindow(current: DualApplePods): Event? = when {
private fun throttleCasePopUps(current: DualApplePods): Event? = when {
current.caseLidState == DualApplePods.LidState.OPEN -> {
log(TAG, INFO) { "Show popup" }
val now = Instant.now()
val lastShown = coolDowns[current.identifier] ?: Instant.MIN
val lastShown = caseCoolDowns[current.identifier] ?: Instant.MIN
val sinceLastPop = Duration.between(lastShown, now)
log(TAG) { "Time since last popup: $sinceLastPop" }
log(TAG) { "Time since last case popup: $sinceLastPop" }
if (sinceLastPop >= Duration.ofSeconds(10)) {
coolDowns[current.identifier] = Instant.now()
caseCoolDowns[current.identifier] = Instant.now()
Event.PopupShow(device = current)
} else {
log(TAG, INFO) { "Popup is still on cooldown: $sinceLastPop" }
log(TAG, INFO) { "Case popup is still on cooldown: $sinceLastPop" }
null
}
}
current.caseLidState != DualApplePods.LidState.OPEN -> {
when (current.caseLidState) {
DualApplePods.LidState.CLOSED -> {
log(TAG, INFO) { "Lid was actively closed, resetting cooldown." }
coolDowns.remove(current.identifier)
caseCoolDowns.remove(current.identifier)
}
else -> {
log(TAG, WARN) { "Lid was was not actively closed, refreshing cooldown." }
coolDowns[current.identifier] = Instant.now()
caseCoolDowns[current.identifier] = Instant.now()
}
}
@@ -90,9 +105,78 @@ class PopUpReaction @Inject constructor(
Event.PopupHide()
}
else -> null
}
private val connectionCoolDowns = mutableMapOf<String, Instant>()
private fun monitorConnection(): Flow<Event> = reactionSettings.showPopUpOnConnection.flow
.flatMapLatest { isEnabled ->
if (!isEnabled) return@flatMapLatest emptyFlow()
combine(
generalSettings.mainDeviceAddress.flow,
bluetoothManager.connectedDevices().distinctUntilChanged(),
podMonitor.mainDevice.distinctUntilChangedBy { it?.rawDataHex },
) { targetAddress, devices, broadcast ->
log(TAG) { "$targetAddress $broadcast $devices " }
val direct = devices.singleOrNull { it.address == targetAddress }.also {
log(TAG, VERBOSE) { "Connected main device is $it" }
}
if (direct == null) {
connectionCoolDowns.remove(targetAddress).also {
if (it != null) log(TAG) { "Cleared connection cooldown for $targetAddress due to disconect" }
}
}
if (direct != null && broadcast != null) direct to broadcast else null
}
}
.withPrevious()
.mapNotNull { (previouss, currents) ->
val previousConnected = previouss?.first
log(TAG, VERBOSE) { "previousConnected: $previousConnected" }
val previousBroadcasted = previouss?.second
log(TAG, VERBOSE) { "previousBroadcasted: $previousBroadcasted" }
val currentConnected = currents?.first
log(TAG, VERBOSE) { "currentConnected: $currentConnected" }
val currentBroadcasted = currents?.second
log(TAG, VERBOSE) { "currentBroadcasted: $currentBroadcasted" }
if (previousConnected != null && previousBroadcasted != null && currentConnected == null) {
return@mapNotNull Event.PopupHide()
}
if (currentConnected == null || currentBroadcasted == null) {
// We need an active connection
return@mapNotNull null
}
val ageOfBroadcastedDevice = Duration.between(Instant.now(), currentBroadcasted.seenFirstAt)
val ageOfConnectedDevice = Duration.between(Instant.now(), currentConnected.seenFirstAt)
if (ageOfBroadcastedDevice > (ageOfConnectedDevice + Duration.ofSeconds(30))) {
// This is likely a false positive, some random nearby device
// We expect the first broadcasts to not be much older than the first connection
log(TAG, VERBOSE) { "Current broadcasted main device is probably a false-positive" }
return@mapNotNull null
}
val now = Instant.now()
val lastShown = connectionCoolDowns[currentConnected.address]
val sinceLastPop = lastShown?.let { Duration.between(it, now) }
log(TAG) { "Time since last connection popup: ${sinceLastPop?.seconds}s" }
if (lastShown == null) {
connectionCoolDowns[currentConnected.address] = Instant.now()
Event.PopupShow(device = currentBroadcasted)
} else {
log(TAG) { "Connection popup is still on cooldown: $sinceLastPop" }
null
}
}
.setupCommonEventHandlers(TAG) { "popUpConnection" }
fun monitor(): Flow<Event> = merge(monitorCase(), monitorConnection())
sealed class Event {
data class PopupShow(
@@ -58,6 +58,4 @@
<string name="pods_case_unknown_state">Невядомы стан</string>
<string name="last_seen_x">Апошні раз быў: %s</string>
<string name="first_seen_x">Упершыню быў: %s</string>
<string name="permission_post_notifications_label">Адлюстраванне апавяшчэнняў</string>
<string name="permission_post_notifications_description">"Дазволіць CAPod адлюстроўваць вам апавяшчэнні аб вашых AirPods: напрыклад, іх бягучы стан пры падключэнні."</string>
</resources>
@@ -58,6 +58,6 @@
<string name="pods_case_unknown_state">Estat desconegut</string>
<string name="last_seen_x">Últim cop vist: %s</string>
<string name="first_seen_x">Primera vegada vist: %s</string>
<string name="permission_post_notifications_label">Publica notificacions</string>
<string name="permission_post_notifications_description">"Permet que el CAPod us mostri notificacions sobre els vostres AirPods, p. ex. el seu estat actual mentre estan connectats."</string>
<string name="permission_post_notifications_label">Mostra notificacions</string>
<string name="permission_post_notifications_description">"Permet que el CAPod mostri notificacions sobre els vostres AirPods, p. ex. el seu estat actual mentre estan connectats."</string>
</resources>
@@ -58,6 +58,6 @@
<string name="pods_case_unknown_state">Neznámý stav</string>
<string name="last_seen_x">Naposledy spatřeno: %s</string>
<string name="first_seen_x">Poprvé spatřeno: %s</string>
<string name="permission_post_notifications_label">Posílat zprávy</string>
<string name="permission_post_notifications_label">Zobrazit oznámení</string>
<string name="permission_post_notifications_description">"Povolte aplikaci CAPod zobrazovat oznámení o sluchátkách AirPods, např. o jejich aktuálním stavu během připojení."</string>
</resources>
@@ -58,6 +58,6 @@
<string name="pods_case_unknown_state">Unbekannter Zustand</string>
<string name="last_seen_x">Zuletzt gesehen: %s</string>
<string name="first_seen_x">Zum ersten Mal gesehen: %s</string>
<string name="permission_post_notifications_label">Benachrichtigungen posten</string>
<string name="permission_post_notifications_description">"Erlauben Sie CAPod, Ihnen Benachrichtigungen über Ihre AirPods anzuzeigen, z.b. ihren aktuellen Status, während sie verbunden sind."</string>
<string name="permission_post_notifications_label">Zeige Benachrichtigungen</string>
<string name="permission_post_notifications_description">"Erlaube CAPod, Benachrichtigungen über deine AirPods anzuzeigen, z. B. deren aktuellen Status, während die Verbindung besteht."</string>
</resources>
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">Δε διατίθεται</string>
<string name="general_error_label">Σφάλμα</string>
<string name="general_grant_permission_action">Παραχώρηση άδειας</string>
@@ -56,4 +58,6 @@
<string name="pods_case_unknown_state">Άγνωστη κατάσταση</string>
<string name="last_seen_x">Τελευταία εμφάνιση: %s</string>
<string name="first_seen_x">Πρώτη εμφάνιση: %s</string>
<string name="permission_post_notifications_label">Εμφάνιση ειδοποιήσεων</string>
<string name="permission_post_notifications_description">"Επιτρέψτε στην εφαρμογή να εμφανίζει ειδοποιήσεις σχετικά με τα AirPods σας, π.χ. την τρέχουσα κατάστασή τους ενώ είστε συνδεδεμένοι."</string>
</resources>
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">CAPod</string>
<string name="general_grant_permission_action">Conceder permiso</string>
<string name="permission_bluetooth_connect_label">Conexión Bluetooth</string>
<string name="permission_bluetooth_connect_description">Esta aplicacion requiere el permiso \"Conexión Bluetooth\" para interactuar con los dispositivos vinculados e iniciar las conexiones.</string>
+32 -25
View File
@@ -1,36 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="general_value_not_available_label">N/D</string>
<string name="general_grant_permission_action">Conceder permiso</string>
<string name="overview_nomaindevice_label">Ningún dispositivo principal</string>
<string name="overview_nomaindevice_description">Es poco probable que todos los dispositivos detectados sean suyos. Encienda y conecte su dispositivo o ajuste la configuración.</string>
<string name="overview_bluetooth_disabled_label">Bluetooth deshabilitado</string>
<string name="overview_bluetooth_disabled_description">El Bluetooth está deshabilitado, habilítelo ;)</string>
<string name="permission_bluetooth_connect_label">Conexión Bluetooth</string>
<string name="permission_bluetooth_connect_description">Esta aplicación requiere el permiso \"conexión Bluetooth\" para interactuar con los dispositivos pareados, e iniciar las conexiones.</string>
<string name="permission_bluetooth_scan_label">Buscando Bluetooth</string>
<string name="permission_bluetooth_scan_description">El permiso \"Escaneo Bluetooth\" permite que esta aplicación descubra y reciba datos de Bluetooth de dispositivos cercanos, como sus AirPods.</string>
<string name="permission_bluetooth_description">Esta aplicacion requiere \"Bluetooth\" para conectarse a los dispositivos pareados.</string>
<string name="permission_access_fine_location_label">Acceso a la ubicación precisa</string>
<string name="permission_access_fine_location_description">CAPod utiliza el permiso de \"ubicación precisa\" para recibir datos de Bluetooth Low Energy. Sus auriculares utilizan la tecnología Bluetooth Low Energy para transmitir su estado. Esta aplicación NO utilizará datos de Bluetooth para determinar su ubicación.</string>
<string name="permission_background_location_label">Acceso a la ubicación en segundo plano</string>
<string name="permission_background_location_description">CAPod usa el \"Acceso a la ubicación en segundo plano\" para habilitar funciones como \"Mostrar ventana emergente\" y \"Conexión automática\" mientras la aplicación está cerrada. El acceso a la ubicación en segundo plano permite que la aplicación reciba datos de Bluetooth Low Energy mientras está en segundo plano. Esta aplicación NO utilizará datos de Bluetooth para determinar su ubicación.</string>
<string name="permission_ignore_battery_optimizations_label">Desactivar Optimización de Batería</string>
<string name="permission_ignore_battery_optimizations_description">La Optimización de Batería evitan que esta aplicación reciba datos de Bluetooth de manera confiable mientras la aplicación está en segundo plano.</string>
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">No disponible</string>
<string name="general_error_label">Fallo</string>
<string name="general_grant_permission_action">Conceder el permiso</string>
<string name="overview_nomaindevice_label">Sin dispositivo principal</string>
<string name="overview_nomaindevice_description">Es poco probable que todos los dispositivos detectados sean tuyos. Enciende y conecta tu dispositivo o ajusta la configuración.</string>
<string name="overview_bluetooth_disabled_label">Bluetooth desactivado</string>
<string name="overview_bluetooth_disabled_description">El Bluetooth está desactivado, actívalo ;)</string>
<string name="permission_bluetooth_connect_label">Conexión por Bluetooth</string>
<string name="permission_bluetooth_connect_description">Esta aplicación requiere el permiso \"Conexión por Bluetooth\" para interactuar con los dispositivos emparejados e iniciar las conexiones.</string>
<string name="permission_bluetooth_scan_label">El Bluetooth está escaneando</string>
<string name="permission_bluetooth_scan_description">El permiso \"Escaneo del Bluetooth\" permite que esta aplicación descubra y reciba datos de Bluetooth de dispositivos cercanos, como tus AirPods.</string>
<string name="permission_bluetooth_label">Bluetooth</string>
<string name="permission_bluetooth_description">Esta aplicación requiere el permiso \"Bluetooth\" para conectarse a dispositivos bluetooth emparejados.</string>
<string name="permission_access_fine_location_label">Acceso preciso a tu ubicación</string>
<string name="permission_access_fine_location_description">CAPod utiliza el permiso \"localización precisa\" para recibir datos Bluetooth Low Energy. Tus auriculares utilizan la tecnología Bluetooth Low Energy para transmitir tu estado. Esta aplicación NO utilizará los datos Bluetooth para determinar tu ubicación.</string>
<string name="permission_background_location_label">Ubicación en segundo plano</string>
<string name="permission_background_location_description">CAPod utiliza el \"la ubicación en segundo plano\" para activar funciones como \"Mostrar ventana emergente\" y \"Conexión automática\" mientras la aplicación está cerrada. El acceso a la localización en segundo plano permite a la aplicación recibir datos de Bluetooth Low Energy mientras está en segundo plano. Esta aplicación NO utilizará los datos Bluetooth para determinar tu ubicación.</string>
<string name="permission_ignore_battery_optimizations_label">Desactivar las optimizaciones de la batería</string>
<string name="permission_ignore_battery_optimizations_description">Las optimizaciones de la batería impiden que esta aplicación reciba datos Bluetooth de forma fiable mientras está en segundo plano.</string>
<string name="permission_required_title">Se requiere el siguiente permiso:</string>
<string name="permission_system_alert_window_label">Ventana de alerta del sistema</string>
<string name="permission_system_alert_window_description">Permita que CAPod dibuje sobre otras aplicaciones para que la función \"Mostrar ventana emergente\" sea posible.</string>
<string name="permission_system_alert_window_description">Permite a CAPod dibujar sobre otras aplicaciones para hacer posible la función \"Mostrar una ventana emergente\".</string>
<string name="settings_scanner_mode_lowpower_label">Bajo consumo</string>
<string name="settings_scanner_mode_balanced_label">Balanceado</string>
<string name="settings_scanner_mode_balanced_label">Equilibrado</string>
<string name="settings_scanner_mode_lowlatency_label">Baja latencia</string>
<string name="settings_monitor_mode_manual_label">Cuando la aplicación está abierta</string>
<string name="settings_monitor_mode_automatic_label">Cuando el dispositivo está conectado</string>
<string name="settings_monitor_mode_always_label">Siempre</string>
<string name="settings_reaction_autoconnect_whenseen_label">Cuando se ve</string>
<string name="settings_reaction_autoconnect_whenseen_label">Cuando está visible</string>
<string name="settings_reaction_autoconnect_caseopen_label">El estuche está abierto</string>
<string name="settings_reaction_autoconnect_inear_label">En el oído</string>
<string name="pods_dual_left_label">Audífono izquierdo</string>
<string name="pods_dual_right_label">Audífono derecho</string>
<string name="pods_dual_left_label">Auricular izquierdo</string>
<string name="pods_dual_right_label">Auricular derecho</string>
<string name="pods_case_label">Estuche</string>
<string name="pods_case_status_open_label">Abierto</string>
<string name="pods_case_status_closed_label">Cerrado</string>
@@ -40,8 +45,8 @@
<string name="pods_connection_state_call_label">En modo llamada</string>
<string name="pods_connection_state_ringing_label">Sonando</string>
<string name="pods_connection_state_hanging_up_label">Colgar</string>
<string name="pods_connection_state_unknown_label">Estado de la conexion desconocido</string>
<string name="pods_unknown_raw_data_label">Datos sin procesar</string>
<string name="pods_connection_state_unknown_label">Estado de la conexión desconocido</string>
<string name="pods_unknown_raw_data_label">Datos no procesados</string>
<string name="pods_unknown_label">Dispositivo desconocido</string>
<string name="pods_unknown_contact_dev">Este es un dispositivo desconocido, pero está usando un formato de mensaje similar. Agreguemos soporte para ello, contácteme :)</string>
<string name="pods_none_label_short">Sin dispositivo</string>
@@ -49,8 +54,10 @@
<string name="pods_inear_label">En el oído</string>
<string name="pods_microphone_label">Micrófono</string>
<string name="pods_yours">Tuyos</string>
<string name="headset_being_worn_label">En uso</string>
<string name="headset_being_worn_label">Utilizándose</string>
<string name="pods_case_unknown_state">Estado desconocido</string>
<string name="last_seen_x">Visto por última vez: %s</string>
<string name="first_seen_x">Visto por primera vez: %s</string>
<string name="permission_post_notifications_label">Mostrar las notificaciones</string>
<string name="permission_post_notifications_description">"Permite que CAPod muestre las notificaciones sobre tus AirPods, por ejemplo, su estado actual mientras están conectados."</string>
</resources>
@@ -6,5 +6,6 @@
<string name="general_value_not_available_label">Pole saadaval</string>
<string name="pods_dual_left_label">Vasak klapp</string>
<string name="pods_dual_right_label">Parem klapp</string>
<string name="permission_post_notifications_label">Näita teateid</string>
<string name="permission_post_notifications_description">"Luba CAPodil näidata AirPodsi teateid, nt praegust olekut ühendatud olles."</string>
</resources>
@@ -19,12 +19,12 @@
<string name="permission_access_fine_location_label">Accéder à la position précise</string>
<string name="permission_access_fine_location_description">CAPod utilise lautorisation « position précise » pour recevoir les données « faible énergie » de Bluetooth. Votre casque-micro utilise la technologie « basse énergie » de Bluetooth pour diffuser son état. Cette appli nutilisera PAS les données Bluetooth pour déterminer votre position.</string>
<string name="permission_background_location_label">Accès à la position en arrière-plan</string>
<string name="permission_background_location_description">CAPod utilise « laccès à la position en arrière-plan » afin de permettre des fonctions telles qu’« Afficher une notification » et « Connexion automatique » alors que lappli est fermée. Laccès à la position en arrière-plan permet à lappli de recevoir les données « basse énergie » de Bluetooth. Elles ne seront PAS utilisées pour déterminer votre position géographie.</string>
<string name="permission_background_location_description">CAPod utilise « laccès à la position en arrière-plan » afin de permettre des fonctions telles qu’« Afficher une fenêtre surgissante » et « Connexion automatique » alors que lappli est fermée. Laccès à la position en arrière-plan permet à lappli de recevoir les données « basse énergie » de Bluetooth. Elles ne seront PAS utilisées pour déterminer votre position géographie.</string>
<string name="permission_ignore_battery_optimizations_label">Désactiver les optimisations de la pile/batterie</string>
<string name="permission_ignore_battery_optimizations_description">Les optimisations de la pile/batterie empêchent lappli de recevoir les données Bluetooth de manière fiable alors que lappli est en arrière-plan.</string>
<string name="permission_required_title">Lautorisation suivante est nécessaire :</string>
<string name="permission_system_alert_window_label">Fenêtre dalerte système</string>
<string name="permission_system_alert_window_description">Permettre à CAPod dapparaître par-dessus les autres applis pour rendre la fonction « Afficher une notification » possible.</string>
<string name="permission_system_alert_window_description">Permettre à CAPod dapparaître par-dessus les autres applis pour rendre la fonction « Afficher une fenêtre surgissante » possible.</string>
<string name="settings_scanner_mode_lowpower_label">Faible puissance</string>
<string name="settings_scanner_mode_balanced_label">Équilibré</string>
<string name="settings_scanner_mode_lowlatency_label">Faible latence</string>
@@ -58,4 +58,6 @@
<string name="pods_case_unknown_state">État inconnu</string>
<string name="last_seen_x">Vu pour la dernière fois : %s</string>
<string name="first_seen_x">Vu pour la première fois : %s</string>
<string name="permission_post_notifications_label">Afficher des notifications</string>
<string name="permission_post_notifications_description">"Permettre à CAPod dafficher des notifications au sujet des AirPods, p. ex. leur état actuel alors quils sont connectés."</string>
</resources>
@@ -2,8 +2,8 @@
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">कैपोड</string>
<string name="app_name_pro">कैपोड प्रो</string>
<string name="app_name_foss">कैपोड फोस</string>
<string name="general_value_not_available_label">एन/ए</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">N/A</string>
<string name="general_error_label">त्रुटि</string>
<string name="general_grant_permission_action">अनुदान अनुमति</string>
<string name="overview_nomaindevice_label">कोई प्राथमिक उपकरण नहीं</string>
@@ -51,4 +51,11 @@
<string name="pods_unknown_contact_dev">यह एक अज्ञात डिवाइस है, लेकिन यह समान संदेश प्रारूप का उपयोग कर रहा है। आइए इसके लिए समर्थन जोड़ें, मुझसे संपर्क करें :)</string>
<string name="pods_none_label_short">उपकरण नहीं</string>
<string name="pods_charging_label">चार्ज</string>
<string name="pods_inear_label">कान में</string>
<string name="pods_microphone_label">माइक्रोफ़ोन</string>
<string name="pods_yours">आपका</string>
<string name="headset_being_worn_label">पहना जा रहा है</string>
<string name="pods_case_unknown_state">अज्ञात अवस्था</string>
<string name="last_seen_x">अंतिम बार देखे गए: %s</string>
<string name="first_seen_x">पहली बार देखा गया: %s</string>
</resources>
@@ -21,21 +21,21 @@
<string name="permission_background_location_label">Accesso in background alla tua posizione</string>
<string name="permission_background_location_description">CAPod usa l\'accesso in background alla tua posizione per far funzionare opzioni come \"Mostra popup\" e \"AutoConnect\" mentre l\'applicazione é chiusa. L\'accesso in background abiliterà l\'applicazione nella ricezione di dati Bluetooth a basso consume mentre l\'app non é aperta. Questa applicazione non utilizza i dati Bluetooth per geolocalizzarti.</string>
<string name="permission_ignore_battery_optimizations_label">Disabilita ottimizzazione batteria</string>
<string name="permission_ignore_battery_optimizations_description">L\'ottimizzazione della batteria impedisce a questa applicazione di ricevere dati Bluetooth mentre l\'app é chiusa.</string>
<string name="permission_ignore_battery_optimizations_description">L\'ottimizzazione della batteria impedisce a questa applicazione di ricevere dati Bluetooth mentre l\'app è in esecuzione in background.</string>
<string name="permission_required_title">È richiesta la seguente autorizzazione:</string>
<string name="permission_system_alert_window_label">Finestra di avviso di sistema</string>
<string name="permission_system_alert_window_description">Permetti a CAPod di far apprarire overlay sopra le altre applicazioni per far funzionare \"Mostra PopUp\".</string>
<string name="permission_system_alert_window_description">Concedi a CAPod la visualizzazione in primo piano sopra le altre applicazioni per la funzione \"Mostra PopUp\".</string>
<string name="settings_scanner_mode_lowpower_label">Basso consumo</string>
<string name="settings_scanner_mode_balanced_label">Bilanciato</string>
<string name="settings_scanner_mode_lowlatency_label">Bassa latenza</string>
<string name="settings_monitor_mode_manual_label">Quando l\'applicazione è aperta</string>
<string name="settings_monitor_mode_automatic_label">Quando il device è connesso</string>
<string name="settings_monitor_mode_automatic_label">Quando le cuffie sono connesse</string>
<string name="settings_monitor_mode_always_label">Sempre</string>
<string name="settings_reaction_autoconnect_whenseen_label">Quando rilevato</string>
<string name="settings_reaction_autoconnect_caseopen_label">La custodia é aperta</string>
<string name="settings_reaction_autoconnect_inear_label">Nell\'orecchio</string>
<string name="pods_dual_left_label">Pod sinistra</string>
<string name="pods_dual_right_label">Pod destra</string>
<string name="pods_dual_left_label">Pod sinistro</string>
<string name="pods_dual_right_label">Pod destro</string>
<string name="pods_case_label">Custodia</string>
<string name="pods_case_status_open_label">Aperto</string>
<string name="pods_case_status_closed_label">Chiuso</string>
@@ -48,7 +48,7 @@
<string name="pods_connection_state_unknown_label">Stato di connessione sconosciuto</string>
<string name="pods_unknown_raw_data_label">Dati non elaborati</string>
<string name="pods_unknown_label">Dispositivo sconosciuto</string>
<string name="pods_unknown_contact_dev">Questo é un dispositivo sconosciutom ma sta utilizzando un formato di comunicazione noto. Aggiungiamo il supporto per questo device, Contattami! :)</string>
<string name="pods_unknown_contact_dev">Questo è un dispositivo sconosciuto, ma sta utilizzando un formato di comunicazione noto. Aggiungiamo il supporto per questo dispositivo, contattami :)</string>
<string name="pods_none_label_short">Nessun dispositivo</string>
<string name="pods_charging_label">In ricarica</string>
<string name="pods_inear_label">Nell\'orecchio</string>
@@ -58,4 +58,6 @@
<string name="pods_case_unknown_state">Stato sconosciuto</string>
<string name="last_seen_x">Ultimo accesso: %s</string>
<string name="first_seen_x">Primo accesso: %s</string>
<string name="permission_post_notifications_label">Mostra notifiche</string>
<string name="permission_post_notifications_description">"Concedi a CAPod di mostrare notifiche riguardanti le tue AirPods, ad esempio il loro stato di carica mentre sono connesse."</string>
</resources>
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod פרו</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">לא זמין</string>
<string name="general_error_label">שגיאה</string>
<string name="general_grant_permission_action">מתן רשות</string>
@@ -55,6 +55,6 @@
<string name="pods_case_unknown_state">Keadaan tidak diketahui</string>
<string name="last_seen_x">Terakhir dilihat: %s</string>
<string name="first_seen_x">Pertama dilihat: %s</string>
<string name="permission_post_notifications_label">Siarkan pemberitahuan</string>
<string name="permission_post_notifications_description">"Benarkan CAPod menunjukkan pemberitahuan kepada anda tentang AirPods anda, mis. status semasa mereka semasa disambungkan."</string>
<string name="permission_post_notifications_label">Tunjukkan pemberitahuanm</string>
<string name="permission_post_notifications_description">"Benarkan CAPod memaparkan pemberitahuan tentang AirPods anda, misalnya, status semasa mereka ketika disambungkan."</string>
</resources>
@@ -32,7 +32,7 @@
<string name="settings_monitor_mode_automatic_label">Wanneer het apparaat is aangesloten</string>
<string name="settings_monitor_mode_always_label">Altijd</string>
<string name="settings_reaction_autoconnect_whenseen_label">Wanneer gezien</string>
<string name="settings_reaction_autoconnect_caseopen_label">Case is geopend</string>
<string name="settings_reaction_autoconnect_caseopen_label">Zaak is geopend</string>
<string name="settings_reaction_autoconnect_inear_label">In oor</string>
<string name="pods_dual_left_label">Linker pod</string>
<string name="pods_dual_right_label">Rechter pod</string>
@@ -58,6 +58,6 @@
<string name="pods_case_unknown_state">Onbekende staat</string>
<string name="last_seen_x">Laatst gezien: %s</string>
<string name="first_seen_x">Eerst gezien: %s</string>
<string name="permission_post_notifications_label">Post notificaties</string>
<string name="permission_post_notifications_description">"Sta CAPod toe notificaties over je AirPods te tonen, b.v. de huidige verbindingsstatus."</string>
<string name="permission_post_notifications_label">Toon meldingen</string>
<string name="permission_post_notifications_description">"Sta CAPod toe om meldingen over je AirPods weer te geven, bijv. hun huidige status terwijl ze verbonden zijn."</string>
</resources>
@@ -58,6 +58,6 @@
<string name="pods_case_unknown_state">Nieznany stan</string>
<string name="last_seen_x">Ostatnio widziane: %s</string>
<string name="first_seen_x">Widziane pierwszy raz: %s</string>
<string name="permission_post_notifications_label">Wysyłanie powiadomień</string>
<string name="permission_post_notifications_label">Wyświetl powiadomienia</string>
<string name="permission_post_notifications_description">"Zezwól CAPod na wyświetlanie powiadomień o słuchawkach AirPods, np. ich aktualnym statusie podczas połączenia."</string>
</resources>
@@ -58,6 +58,4 @@
<string name="pods_case_unknown_state">Estado desconhecido</string>
<string name="last_seen_x">Visto pela última vez: %s</string>
<string name="first_seen_x">Visto pela primeira vez: %s</string>
<string name="permission_post_notifications_label">Post de notificações</string>
<string name="permission_post_notifications_description">"Permita que o CAPod mostre notificações sobre seus AirPods, por exemplo. seu status atual enquanto conectado."</string>
</resources>
@@ -58,4 +58,6 @@
<string name="pods_case_unknown_state">Estado desconhecido</string>
<string name="last_seen_x">Última vez visto: %s</string>
<string name="first_seen_x">Primeira vez visto: %s</string>
<string name="permission_post_notifications_label">Enviar notificações</string>
<string name="permission_post_notifications_description">"Permitir que o CAPod envie notificações sobre os seus AirPods, por exemplo, o seu estado atual enquanto conectado."</string>
</resources>
@@ -58,5 +58,6 @@
<string name="pods_case_unknown_state">Неизвестное состояние</string>
<string name="last_seen_x">Последние: %s</string>
<string name="first_seen_x">Первые: %s</string>
<string name="permission_post_notifications_description">"Разрешить CAPod показывать уведомления о ваших AirPods, например текущий статус при подключении."</string>
<string name="permission_post_notifications_label">Показывать уведомления</string>
<string name="permission_post_notifications_description">"Разрешить CAPod показывать уведомления о Ваших AirPods, например текущий статус при подключении."</string>
</resources>
@@ -59,5 +59,5 @@
<string name="last_seen_x">Naposledy videné: %s</string>
<string name="first_seen_x">Prvýkrát videné: %s</string>
<string name="permission_post_notifications_label">Zobraziť oznámenia</string>
<string name="permission_post_notifications_description">"Povoľte CAPod, aby vám zobrazoval upozornenia týkajúce sa vašich slúchadiel AirPods, napr. ich aktuálny stav počas pripojenia."</string>
<string name="permission_post_notifications_description">"Povoľte CAPod, aby zobrazoval upozornenia týkajúce sa vašich slúchadiel AirPods, napr. ich aktuálny stav počas pripojenia."</string>
</resources>
+30 -28
View File
@@ -3,59 +3,61 @@
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">Yok</string>
<string name="general_value_not_available_label">Kapalı</string>
<string name="general_error_label">Hata</string>
<string name="general_grant_permission_action">İzin ver</string>
<string name="overview_nomaindevice_label">Birincil aygıt yok</string>
<string name="overview_nomaindevice_description">Algılanan tüm cihazların sizin olması olası değildir. Cihazınızı açın ve bağlayın veya ayarları yapın.</string>
<string name="overview_bluetooth_disabled_label">Bluetooth devre dışı</string>
<string name="overview_bluetooth_disabled_description">Bluetooth devre dışı, etkinleştirin ;)</string>
<string name="overview_nomaindevice_description">Tespit edilen tüm aygıtların sizin olması muhtemel değildir. Aygıtınızı açın ve bağlayın veya ayarları yapın.</string>
<string name="overview_bluetooth_disabled_label">Bluetooth kapalı</string>
<string name="overview_bluetooth_disabled_description">Lütfen etkinleştirin ;)</string>
<string name="permission_bluetooth_connect_label">Bluetooth bağlantısı</string>
<string name="permission_bluetooth_connect_description">Bu uygulama, eşleştirilmiş cihazlarla etkileşim kurmak ve bağlantıları başlatmak için \"Bluetooth bağlantısı\" izni gerektirir.</string>
<string name="permission_bluetooth_connect_description">Bu uygulama, eşleştirilmiş aygıtlarla etkileşime geçmek ve bağlantıları başlatmak için \"Bluetooth bağlantısı\" izni gerektirir.</string>
<string name="permission_bluetooth_scan_label">Bluetooth taraması</string>
<string name="permission_bluetooth_scan_description">\"Bluetooth tarama\" izni, bu uygulamanın AirPods\'unuz gibi yakındaki cihazlardan Bluetooth verilerini keşfetmesine ve almasına izin verir.</string>
<string name="permission_bluetooth_scan_description">\"Bluetooth tarama\" izni, bu uygulamanın AirPod\'larınız gibi yakınınızdaki aygıtlardan Bluetooth verilerini keşfetmesine ve almasına olanak tanır.</string>
<string name="permission_bluetooth_label">Bluetooth</string>
<string name="permission_bluetooth_description">Bu uygulama, eşleştirilmiş bluetooth cihazlarına bağlanmak için \"Bluetooth\" izni gerektirir.</string>
<string name="permission_access_fine_location_label">Hassas konuma erişin</string>
<string name="permission_access_fine_location_description">CAPod, Bluetooth Düşük Enerji verilerini almak için \"iyi konum\" iznini kullanır. Kulaklıklarınız durumlarını yayınlamak için Bluetooth Düşük Enerji teknolojisini kullanır. Bu uygulama konumunuzu belirlemek için Bluetooth verilerini KULLANMAYACAKTIR.</string>
<string name="permission_bluetooth_description">Bu uygulama, eşleştirilmiş bluetooth aygıtlarına bağlanmak için \"Bluetooth\" izni gerektirir.</string>
<string name="permission_access_fine_location_label">Tam konuma erişim</string>
<string name="permission_access_fine_location_description">CAPod, Bluetooth Düşük Enerji verilerini almak için \"tam konum\" iznini kullanır. Kulaklığınız, durumlarını yayınlamak için Bluetooth Düşük Enerji teknolojisini kullanır. Bu uygulama, konumunuzu belirlemek için Bluetooth verilerini KULLANMAZ.</string>
<string name="permission_background_location_label">Arka planda konum erişimi</string>
<string name="permission_background_location_description">CAPod, uygulama kapalıyken \"Açılır pencereyi göster\" ve \"Otomatik Bağlantı\" gibi özellikleri etkinleştirmek için \"arka plan konumu erişimi\" kullanır. Arka plan konumu erişimi, uygulamanın arka plandayken Bluetooth Düşük Enerji verilerini almasını sağlar. Bu uygulama konumunuzu belirlemek için Bluetooth verilerini KULLANMAZ.</string>
<string name="permission_ignore_battery_optimizations_label">Pil optimizasyonlarını devre dışı bırak</string>
<string name="permission_ignore_battery_optimizations_description">Pil iyileştirmeleri, uygulama arka plandayken bu uygulamanın Bluetooth verilerini güvenilir bir şekilde almasını engeller.</string>
<string name="permission_background_location_description">CAPod, uygulama kapalıyken \"Açılır pencereyi göster\" ve \"Otomatik Bağlantı\" gibi özellikleri etkinleştirmek için \"arka planda konum erişimi\"ni kullanır. Arka planda konum erişimi, uygulamanın arka plandayken Bluetooth Düşük Enerji verilerini almasına olanak tanır. Bu uygulama, konumunuzu belirlemek için Bluetooth verilerini KULLANMAZ.</string>
<string name="permission_ignore_battery_optimizations_label">Pil optimizasyonunu devre dışı bırak</string>
<string name="permission_ignore_battery_optimizations_description">Pil optimizasyonları, uygulamanın arka planda çalışırken güvenilir bir şekilde Bluetooth verilerini almasını engeller.</string>
<string name="permission_required_title">Aşağıdaki izin gereklidir:</string>
<string name="permission_system_alert_window_label">Sistem Uyarı Penceresi</string>
<string name="permission_system_alert_window_description">CAPod\'un \"PopUp\'ı Göster\" özelliğini mümkün kılmak için diğer uygulamaların üzerinde çizmesine izin verin.</string>
<string name="permission_system_alert_window_description">\"Açılır pencereyi göster\" özelliğine izin vermek için lütfen CAPod uygulamasının diğer uygulamaların üzerinde gösterilmesine izin verin.</string>
<string name="settings_scanner_mode_lowpower_label">Düşük güç</string>
<string name="settings_scanner_mode_balanced_label">Dengeli</string>
<string name="settings_scanner_mode_lowlatency_label">Düşük gecikme süresi</string>
<string name="settings_scanner_mode_lowlatency_label">Düşük gecikme</string>
<string name="settings_monitor_mode_manual_label">Uygulama açıkken</string>
<string name="settings_monitor_mode_automatic_label">Aygıt bağlandığında</string>
<string name="settings_monitor_mode_always_label">Her zaman</string>
<string name="settings_reaction_autoconnect_whenseen_label">Görüldüğünde</string>
<string name="settings_reaction_autoconnect_caseopen_label">Vaka açık</string>
<string name="settings_reaction_autoconnect_caseopen_label">Şarj kutusu açık</string>
<string name="settings_reaction_autoconnect_inear_label">Kulakta</string>
<string name="pods_dual_left_label">Sol bölme</string>
<string name="pods_dual_right_label">Sağ bölme</string>
<string name="pods_case_label">Sorun</string>
<string name="pods_dual_left_label">Sol kulaklık</string>
<string name="pods_dual_right_label">Sağ kulaklık</string>
<string name="pods_case_label">Şarj kutusu</string>
<string name="pods_case_status_open_label">Açık</string>
<string name="pods_case_status_closed_label">Kapalı</string>
<string name="pods_connection_state_disconnected_label">Bir cihaza bağlı değil</string>
<string name="pods_connection_state_idle_label">Bir cihaza bağlı, ancak boşta</string>
<string name="pods_connection_state_disconnected_label">Bir aygıta bağlı değil</string>
<string name="pods_connection_state_idle_label">Bir aygıta bağlı, ancak boşta</string>
<string name="pods_connection_state_music_label">Müzik modunda</string>
<string name="pods_connection_state_call_label">Arama modunda</string>
<string name="pods_connection_state_ringing_label">Çalıyor</string>
<string name="pods_connection_state_hanging_up_label">Kapatıyorum</string>
<string name="pods_connection_state_hanging_up_label">Kapatılıyor</string>
<string name="pods_connection_state_unknown_label">Bilinmeyen bağlantı durumu</string>
<string name="pods_unknown_raw_data_label">İşlenmemiş veri</string>
<string name="pods_unknown_label">Tanımlanmamış cihaz</string>
<string name="pods_unknown_contact_dev">Bu bilinmeyen bir cihazdır, ancak benzer bir mesaj formatı kullanıyor. Bunun için destek ekleyelim, bana ulaşın :)</string>
<string name="pods_none_label_short">Cihaz yok</string>
<string name="pods_charging_label">Doluyor</string>
<string name="pods_unknown_raw_data_label">Ham veri</string>
<string name="pods_unknown_label">Bilinmeyen aygıt</string>
<string name="pods_unknown_contact_dev">Bu bilinmeyen bir aygıt ama benzer mesaj formatı kullanıyor. Hadi buna destek ekleyelim, bana ulaşın :)</string>
<string name="pods_none_label_short">Aygıt yok</string>
<string name="pods_charging_label">Şarj oluyor</string>
<string name="pods_inear_label">Kulakta</string>
<string name="pods_microphone_label">Mikrofon</string>
<string name="pods_yours">Senin</string>
<string name="headset_being_worn_label">Giyilmek</string>
<string name="pods_yours">Sizinki</string>
<string name="headset_being_worn_label">Takılıyken</string>
<string name="pods_case_unknown_state">Bilinmeyen durum</string>
<string name="last_seen_x">Son görülme: %s</string>
<string name="first_seen_x">İlk görülme: %s</string>
<string name="permission_post_notifications_label">Bildirimleri göster</string>
<string name="permission_post_notifications_description">"CAPod'un AirPods'larınız hakkında bildirimler göstermesine izin vermelisiniz, ör. cihaza bağlıyken mevcut durumları."</string>
</resources>
+23 -2
View File
@@ -1,23 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="general_value_not_available_label">Н</string>
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">Н</string>
<string name="general_error_label">Помилка</string>
<string name="general_grant_permission_action">Надати дозвіл</string>
<string name="overview_nomaindevice_label">Відсутній первинний пристрій</string>
<string name="overview_nomaindevice_description">Всі знайдені пристрої навряд чи ваші. Увімкніть та підʼєднайте ваш пристрій або налаштуйте параметри.</string>
<string name="overview_bluetooth_disabled_label">Bluetooth вимкнено</string>
<string name="overview_bluetooth_disabled_description">Bluetooth вимкнено, увімкніть його ;)</string>
<string name="permission_bluetooth_connect_label">Bluetooth зʼєднання</string>
<string name="permission_bluetooth_connect_description">Цей додаток потребує дозвіл \"Bluetooth зʼєднання\" щоб взаємодіяти зі сполученими пристроями та ініціювати нові зʼєднання.</string>
<string name="permission_bluetooth_scan_label">Сканування Bluetooth</string>
<string name="permission_bluetooth_scan_description">Дозвіл \"Сканування Bluetooth\" дозволяє цьому додатку виявляти та отримувати Bluetooth дані з пристроїв поблизу, таких як ваші AirPods.</string>
<string name="permission_bluetooth_label">Bluetooth</string>
<string name="permission_bluetooth_description">Цей додаток потребує дозвіл \"Bluetooth\" для зʼєднання зі сполученими Bluetooth пристроями.</string>
<string name="permission_access_fine_location_label">Доступ до точного місцезнаходження</string>
<string name="permission_access_fine_location_description">CAPod використовує дозвіл \"Точне місцезнаходження\" щоб отримувати дані Bluetooth Low Energy. Ваші навушники використовують технологію Bluetooth Low Energy technology щоб транслювати свій статус. Цей додаток НЕ використовує Bluetooth дані щоб визначати ваше місцеперебування.</string>
<string name="permission_background_location_label">Доступ до місцезнаходження у тлі</string>
<string name="permission_background_location_description">CAPod використовує фоновий доступ до геоданих для роботи таких функцій як \"Показувати спливаюче вікно\" та \"Автопідʼєднання\" коли додаток закритий. Фоновий доступ до геоданих дозволяє додатку отримувати дані Bluetooth Low Energy коли він працює у фоновому режимі. Цей додаток НЕ використовує Bluetooth дані щоб визначати ваше місцеперебування.</string>
<string name="permission_ignore_battery_optimizations_label">Вимкнути оптимізацію батареї</string>
<string name="permission_ignore_battery_optimizations_description">Оптимізація батареї перешкоджає цьому пристрою надійно отримувати дані Bluetooth під час перебування програми у тлі.</string>
<string name="permission_required_title">Необхідні наступні дозволи:</string>
<string name="permission_system_alert_window_label">Вікно системного повідомлення</string>
<string name="settings_scanner_mode_lowpower_label">Низький заряд</string>
<string name="permission_system_alert_window_description">Надайте дозвіл CAPod на показ поверх інших додатків для роботи функції \"Показувати спливаюче вікно\".</string>
<string name="settings_scanner_mode_lowpower_label">Економія енергії</string>
<string name="settings_scanner_mode_balanced_label">Збалансовано</string>
<string name="settings_scanner_mode_lowlatency_label">Низька затримка</string>
<string name="settings_monitor_mode_manual_label">Коли програму відкрито</string>
<string name="settings_monitor_mode_automatic_label">При під\'єднаному пристрої</string>
<string name="settings_monitor_mode_always_label">Завжди</string>
<string name="settings_reaction_autoconnect_whenseen_label">При виявленні</string>
<string name="settings_reaction_autoconnect_caseopen_label">Футляр відчинено</string>
<string name="settings_reaction_autoconnect_inear_label">У вусі</string>
<string name="pods_dual_left_label">Лівий навушник</string>
@@ -26,9 +40,13 @@
<string name="pods_case_status_open_label">Відкрито</string>
<string name="pods_case_status_closed_label">Закрито</string>
<string name="pods_connection_state_disconnected_label">Не під\'єднано до пристрою</string>
<string name="pods_connection_state_idle_label">Підʼєднано до пристрою, в режимі очікування</string>
<string name="pods_connection_state_music_label">У режимі музики</string>
<string name="pods_connection_state_call_label">У режимі виклику</string>
<string name="pods_connection_state_ringing_label">Дзвінок</string>
<string name="pods_connection_state_hanging_up_label">Кінець дзвінка</string>
<string name="pods_connection_state_unknown_label">Невідомий стан з\'єднання</string>
<string name="pods_unknown_raw_data_label">Сирі дані</string>
<string name="pods_unknown_label">Невідомий пристрій</string>
<string name="pods_unknown_contact_dev">Це невідомий пристрій, проте він використовує подібний формат повідомлень. Додаймо його підтримку, надішліть мені лист :)</string>
<string name="pods_none_label_short">Немає пристрою</string>
@@ -36,7 +54,10 @@
<string name="pods_inear_label">У вусі</string>
<string name="pods_microphone_label">Мікрофон</string>
<string name="pods_yours">Ваш</string>
<string name="headset_being_worn_label">Одягнені</string>
<string name="pods_case_unknown_state">Невідомий стан</string>
<string name="last_seen_x">Востаннє був: %s</string>
<string name="first_seen_x">Уперше був: %s</string>
<string name="permission_post_notifications_label">Показувати сповіщення</string>
<string name="permission_post_notifications_description">"Дозволити CAPod показ сповіщень щодо ваших AirPods, наприклад, їх поточний стан при підключенні."</string>
</resources>
@@ -53,4 +53,6 @@
<string name="pods_case_unknown_state">Trạng thái không xác định</string>
<string name="last_seen_x">Nhìn thấy lần cuối: %s</string>
<string name="first_seen_x">Nhìn thấy lần đầu: %s</string>
<string name="permission_post_notifications_label">Hiển thị thông báo</string>
<string name="permission_post_notifications_description">"Cho phép CAPod hiển thị thông báo về AirPods của bạn, ví dụ: tình trạng hiện tại của họ trong khi kết nối."</string>
</resources>
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">不可用</string>
<string name="general_error_label">错误</string>
<string name="general_grant_permission_action">授予权限</string>
@@ -55,4 +58,6 @@
<string name="pods_case_unknown_state">未知状态</string>
<string name="last_seen_x">上次出现:%s</string>
<string name="first_seen_x">首次出现:%s</string>
<string name="permission_post_notifications_label">显示通知</string>
<string name="permission_post_notifications_description">"允许 CAPod 显示有关您 AirPods 的通知,例如其连接时的当前状态"</string>
</resources>
@@ -1,2 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"></resources>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_name">CAPod</string>
<string name="app_name_pro">CAPod Pro</string>
<string name="app_name_foss">CAPod FOSS</string>
<string name="general_value_not_available_label">不適用</string>
<string name="general_error_label">錯誤</string>
<string name="general_grant_permission_action">授予權限</string>
<string name="overview_nomaindevice_label">無主要裝置</string>
<string name="overview_nomaindevice_description">所有偵測到的裝置似乎並不是您的。開啟並連線至您的裝置,也可以調整設定。</string>
<string name="overview_bluetooth_disabled_label">藍牙已停用</string>
<string name="overview_bluetooth_disabled_description">藍牙已停用,啟用它 ;)</string>
<string name="permission_bluetooth_connect_label">藍牙連線</string>
<string name="permission_bluetooth_connect_description">這個應用程式需要「藍牙連線」權限以與已配對的裝置交互或配對新裝置。</string>
<string name="permission_bluetooth_scan_label">藍牙掃描</string>
<string name="permission_bluetooth_scan_description">「藍牙掃描」權限允許這個應用程式發現附近的裝置並接收藍牙資料,您的 AirPods 需要如此。</string>
<string name="permission_bluetooth_label">藍牙</string>
<string name="permission_bluetooth_description">這個應用程式需要「藍牙」權限與已配對裝置連線。</string>
<string name="permission_access_fine_location_label">存取精確位置</string>
<string name="permission_access_fine_location_description">CAPod 使用「精確位置」權限以接收低功耗藍牙資料。您的耳機使用低功耗藍牙技術以廣播其狀態。這個應用程式不會使用藍牙資料來確定您的位置。</string>
<string name="permission_background_location_label">背景位置存取</string>
<string name="permission_background_location_description">CAPods 在應用程式關閉時使用「背景位置存取」來啟用諸如「顯示彈出式視窗」和「自動連線」等功能。背景位置存取允許這個應用程式在背景接收低功耗藍牙資料。這個應用程式不會使用藍牙資料來確定您的位置。</string>
<string name="permission_ignore_battery_optimizations_label">停用電池效能最佳化</string>
<string name="permission_ignore_battery_optimizations_description">電池效能最佳化使這個應用程式在背景時無法可靠地接收藍牙資料。</string>
<string name="permission_required_title">可能要求下列權限:</string>
<string name="permission_system_alert_window_label">系統警報視窗</string>
<string name="permission_system_alert_window_description">允許 CAPod 在其他應用程式上繪圖,使「顯示彈出式視窗」功能成為可能。</string>
<string name="settings_scanner_mode_lowpower_label">低電源</string>
<string name="settings_scanner_mode_balanced_label">平衡</string>
<string name="settings_scanner_mode_lowlatency_label">低延遲</string>
<string name="settings_monitor_mode_manual_label">應用程式開啟時</string>
<string name="settings_monitor_mode_automatic_label">裝置連線時</string>
<string name="settings_monitor_mode_always_label">一律</string>
<string name="settings_reaction_autoconnect_whenseen_label">看到時</string>
<string name="settings_reaction_autoconnect_caseopen_label">充電盒開啟</string>
<string name="settings_reaction_autoconnect_inear_label">在耳中</string>
<string name="pods_dual_left_label">左耳</string>
<string name="pods_dual_right_label">右耳</string>
<string name="pods_case_label">充電盒</string>
<string name="pods_case_status_open_label">已開啟</string>
<string name="pods_case_status_closed_label">已關閉</string>
<string name="pods_connection_state_disconnected_label">未連線到裝置</string>
<string name="pods_connection_state_idle_label">已連線到裝置,閒置中</string>
<string name="pods_connection_state_music_label">音樂模式</string>
<string name="pods_connection_state_call_label">呼叫模式</string>
<string name="pods_connection_state_ringing_label">振鈴</string>
<string name="pods_connection_state_hanging_up_label">掛機</string>
<string name="pods_connection_state_unknown_label">未知連線狀態</string>
<string name="pods_unknown_raw_data_label">原始資料</string>
<string name="pods_unknown_label">未知裝置</string>
<string name="pods_unknown_contact_dev">這是一個未知裝置,但可以使用相似的訊息格式。聯絡我以為此裝置新增相關支援 :)</string>
<string name="pods_none_label_short">無裝置</string>
<string name="pods_charging_label">充電中</string>
<string name="pods_inear_label">在耳中</string>
<string name="pods_microphone_label">麥克風</string>
<string name="pods_yours">您的</string>
<string name="headset_being_worn_label">正被佩戴</string>
<string name="pods_case_unknown_state">未知狀態</string>
<string name="last_seen_x">最後連線:%s</string>
<string name="first_seen_x">首次連線:%s</string>
<string name="permission_post_notifications_label">顯示通知</string>
<string name="permission_post_notifications_description">"允許 CAPod 顯示關於您 AirPods 的通知,例如在連線時顯示它們的目前狀態。"</string>
</resources>
@@ -58,6 +58,6 @@
<string name="pods_case_unknown_state">未知狀態</string>
<string name="last_seen_x">最後連線:%s</string>
<string name="first_seen_x">首次連線:%s</string>
<string name="permission_post_notifications_label">張貼通知</string>
<string name="permission_post_notifications_description">"允許 CAPod 為您顯示關於您 AirPods 的通知,例如在連線時顯示它們的目前狀態。"</string>
<string name="permission_post_notifications_label">顯示通知</string>
<string name="permission_post_notifications_description">"允許 CAPod 顯示關於您 AirPods 的通知,例如在連線時顯示它們的目前狀態。"</string>
</resources>
+2 -2
View File
@@ -65,6 +65,6 @@
<string name="last_seen_x">Last seen: %s</string>
<string name="first_seen_x">First seen: %s</string>
<string name="permission_post_notifications_label">Post notifications</string>
<string name="permission_post_notifications_description">"Allow CAPod to show you notifications about your AirPods, e.g. their current status while connected."</string>
<string name="permission_post_notifications_label">Show notifications</string>
<string name="permission_post_notifications_description">"Allow CAPod to show notifications about your AirPods, e.g. their current status while connected."</string>
</resources>
+6 -9
View File
@@ -9,6 +9,7 @@ apply(plugin = "androidx.navigation.safeargs.kotlin")
android {
compileSdk = ProjectConfig.compileSdk
namespace = "${ProjectConfig.packageName}"
defaultConfig {
applicationId = ProjectConfig.packageName
@@ -44,12 +45,9 @@ android {
create("gplay") {
dimension = "version"
signingConfig = signingConfigs["releaseGplay"]
extra["useBugsnag"] = true
}
}
setupBugsnagPlugin()
buildTypes {
val customProguardRules = fileTree(File("../proguard")) {
include("*.pro")
@@ -102,12 +100,12 @@ android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
freeCompilerArgs = freeCompilerArgs + listOf(
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
@@ -132,13 +130,12 @@ dependencies {
addBaseAndroid()
addBaseAndroidUi()
implementation("androidx.core:core-splashscreen:1.0.1")
addNavigation()
addTesting()
implementation("androidx.wear:wear:1.2.0")
implementation("androidx.wear.tiles:tiles-material:1.1.0")
"gplayImplementation"("com.bugsnag:bugsnag-android:5.9.2")
"gplayImplementation"("com.getkeepsafe.relinker:relinker:1.4.3")
}
@@ -0,0 +1,18 @@
package eu.darken.capod.wear.core
import android.content.Context
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Locale
data class UserTime(
val time: Instant = Instant.now(),
) {
fun toFormatted(context: Context): String = DateTimeFormatter
.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(Locale.getDefault())
.withZone(ZoneId.systemDefault())
.format(time)
}
@@ -17,6 +17,7 @@ import eu.darken.capod.main.core.PermissionTool
import eu.darken.capod.monitor.core.PodMonitor
import eu.darken.capod.pods.core.DualPodDevice
import eu.darken.capod.pods.core.SinglePodDevice
import eu.darken.capod.wear.core.UserTime
import eu.darken.capod.wear.ui.overview.cards.BluetoothDisabledVH
import eu.darken.capod.wear.ui.overview.cards.MissingMainDeviceVH
import eu.darken.capod.wear.ui.overview.cards.PermissionCardVH
@@ -86,14 +87,20 @@ class OverviewFragmentVM @Inject constructor(
device = podToShow,
showDebug = isDebugMode,
isMainPod = true,
userTime = UserTime(),
)
is SinglePodDevice -> SinglePodsCardVH.Item(
now = now,
device = podToShow,
showDebug = isDebugMode,
isMainPod = true,
userTime = UserTime(),
)
else -> MissingMainDeviceVH.Item(
userTime = UserTime(),
)
else -> MissingMainDeviceVH.Item
}
items.add(pod)
@@ -5,6 +5,7 @@ import eu.darken.capod.R
import eu.darken.capod.common.lists.binding
import eu.darken.capod.common.lists.differ.DifferItem
import eu.darken.capod.databinding.OverviewNomaindeviceItemBinding
import eu.darken.capod.wear.core.UserTime
import eu.darken.capod.wear.ui.overview.OverviewAdapter
class MissingMainDeviceVH(parent: ViewGroup) :
@@ -21,11 +22,14 @@ class MissingMainDeviceVH(parent: ViewGroup) :
item: Item,
payloads: List<Any>
) -> Unit = binding(payload = true) { item ->
userTime.text = item.userTime.toFormatted(context)
}
object Item : OverviewAdapter.Item {
override val stableId: Long = Item::class.hashCode().toLong()
data class Item(
val userTime: UserTime,
) : OverviewAdapter.Item {
override val stableId: Long = this.hashCode().toLong()
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)
get() = { old, new -> if (new::class.isInstance(old)) new else null }
@@ -9,6 +9,7 @@ import eu.darken.capod.common.lists.binding
import eu.darken.capod.databinding.OverviewPodsDualItemBinding
import eu.darken.capod.pods.core.*
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.wear.core.UserTime
import java.time.Instant
class DualPodsCardVH(parent: ViewGroup) :
@@ -20,7 +21,10 @@ class DualPodsCardVH(parent: ViewGroup) :
override val viewBinding = lazy { OverviewPodsDualItemBinding.bind(itemView) }
override val onBindData = binding(payload = true) { item: Item ->
userTime.text = item.userTime.toFormatted(context)
val device = item.device
name.apply {
val sb = StringBuilder(device.getLabel(context))
if (device is HasPodStyle && item.showDebug) {
@@ -127,5 +131,6 @@ class DualPodsCardVH(parent: ViewGroup) :
override val device: DualPodDevice,
override val showDebug: Boolean,
override val isMainPod: Boolean,
val userTime: UserTime,
) : PodDeviceVH.Item
}
@@ -3,13 +3,12 @@ package eu.darken.capod.wear.ui.overview.cards.pods
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.viewbinding.ViewBinding
import eu.darken.capod.R
import eu.darken.capod.common.lists.BindableVH
import eu.darken.capod.common.lists.differ.DifferItem
import eu.darken.capod.common.lists.modular.ModularAdapter
import eu.darken.capod.wear.ui.overview.OverviewAdapter
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.getSignalQuality
import eu.darken.capod.wear.ui.overview.OverviewAdapter
import java.time.Instant
abstract class PodDeviceVH<D : PodDeviceVH.Item, B : ViewBinding>(
@@ -19,7 +18,7 @@ abstract class PodDeviceVH<D : PodDeviceVH.Item, B : ViewBinding>(
fun Item.getReceptionText(): String = device.getSignalQuality(context)
.let { if (showDebug) "$it ${device.seenCounter}" else it }
.let { if (isMainPod) "$it\n(${getString(R.string.pods_yours)})" else it }
.let { if (isMainPod) "$it\n(${getString(eu.darken.capod.common.R.string.pods_yours)})" else it }
interface Item : OverviewAdapter.Item {
@@ -8,6 +8,7 @@ import eu.darken.capod.R
import eu.darken.capod.common.lists.binding
import eu.darken.capod.databinding.OverviewPodsSingleItemBinding
import eu.darken.capod.pods.core.*
import eu.darken.capod.wear.core.UserTime
import java.time.Instant
class SinglePodsCardVH(parent: ViewGroup) :
@@ -19,6 +20,8 @@ class SinglePodsCardVH(parent: ViewGroup) :
override val viewBinding = lazy { OverviewPodsSingleItemBinding.bind(itemView) }
override val onBindData = binding(payload = true) { item: Item ->
userTime.text = item.userTime.toFormatted(context)
val device = item.device
name.apply {
@@ -75,5 +78,6 @@ class SinglePodsCardVH(parent: ViewGroup) :
override val device: SinglePodDevice,
override val showDebug: Boolean,
override val isMainPod: Boolean,
val userTime: UserTime,
) : PodDeviceVH.Item
}
@@ -1,44 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M400.08,358.82l-220.33,59a58.52,58.52 0,0 1,-71.68 -41.38l-5,-18.53a171.22,171.22 0,0 1,24.44 -141.76l-23.43,-23.5a25,25 0,0 1,0 -35.35l0.82,-0.82a24.87,24.87 0,0 1,35.24 0l22.27,22.34a170.85,170.85 0,0 1,62 -31.09l0.48,-0.13a172.54,172.54 0,0 1,71.4 -3.89l8.63,-31.85a25.14,25.14 0,0 1,30.7 -17.66l1.13,0.29A24.8,24.8 0,0 1,354.42 125l-9.23,34.07a172.66,172.66 0,0 1,91.7 111l4.58,17.08A58.54,58.54 0,0 1,400.08 358.82Z"
android:fillColor="#41eb8e"
android:fillType="evenOdd" />
<path
android:pathData="M279.56,314.16c-13.7,-8 -31.7,-7.91 -45.66,-0.68L212,351.37c0.68,15.16 9.41,30.14 24.29,38.79a49.92,49.92 0,0 0,14.87 5.76l-20.81,36.44a8.28,8.28 0,0 0,3 11.33l12.8,7.44a8.26,8.26 0,0 0,11.31 -3l41.73,-72.75h0C310.82,354.32 306.77,330 279.56,314.16ZM247.67,347.24a6,6 0,1 1,-2.16 -8.26A6,6 0,0 1,247.67 347.24Z"
android:fillColor="#fff"
android:fillType="evenOdd" />
<path
android:pathData="M300.66,214.18a27.95,27.95 0,1 1,-19.8 34.24A28,28 0,0 1,300.66 214.18Z"
android:fillColor="#26845c"
android:fillType="evenOdd" />
<path
android:pathData="M194.53,242.62a27.95,27.95 0,1 1,-19.8 34.24A28,28 0,0 1,194.53 242.62Z"
android:fillColor="#26845c"
android:fillType="evenOdd" />
<path
android:pathData="M149.79,318.88l-36.3,-21.21a50.22,50.22 0,0 0,10 -12.43c8.56,-14.94 8.61,-32.3 1.59,-45.79l-38,-21.9c-15.72,0.7 -31.35,9.65 -39.22,23.41 -15.64,27.32 -6.94,50.45 13.74,62.85h0l72.64,42.25a8.26,8.26 0,0 0,11.31 -3l7.36,-12.85A8.31,8.31 0,0 0,149.79 318.88ZM100.3,251.53A6.06,6.06 0,1 1,98 243.28,6.07 6.07,0 0,1 100.3,251.53Z"
android:fillColor="#fff"
android:fillType="evenOdd" />
<path
android:pathData="M187.92,204.08a29.52,29.52 0,0 1,-38.6 -15.52A29.15,29.15 0,0 1,165 150.28a30.38,30.38 0,0 1,4.68 -1.52l-16.36,-38.48L148.6,99.07l-4.77,-11.21 40,-16.75 9.54,22.42L176,100.79l27.58,64.87 0.14,0.34a28.56,28.56 0,0 1,1.24 3.65A29.16,29.16 0,0 1,187.92 204.08Z"
android:fillColor="#ffda3e"
android:fillType="evenOdd" />
<path
android:pathData="M259.45,46l-0.77,13.75 -10.63,-0.57L245.84,99l-0.13,2.29h-0.16A16.6,16.6 0,1 1,232.87 82l1.32,-23.6h0l0.39,-6.87 0.38,-6.88Z"
android:fillColor="#ffcf3b"
android:fillType="evenOdd" />
<path
android:pathData="M205.22,169.54l-0.26,0.11a28.56,28.56 0,0 0,-1.24 -3.65Z"
android:fillColor="#e7b720"
android:fillType="evenOdd" />
<path
android:pathData="M95.05,247.69m-7.01,0a7.01,7.01 0,1 1,14.02 0a7.01,7.01 0,1 1,-14.02 0"
android:fillColor="#231f20" />
<path
android:pathData="M242.42,343.4m-7.01,0a7.01,7.01 0,1 1,14.02 0a7.01,7.01 0,1 1,-14.02 0"
android:fillColor="#231f20" />
</vector>
@@ -1,42 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="m256,13c134.21,0 243,108.79 243,243c0,134.21 -108.79,243 -243,243c-134.21,0 -243,-108.79 -243,-243c0,-134.21 108.79,-243 243,-243z"
android:fillColor="#3f7aff" />
<path
android:pathData="m404.3,630.55l-93.18,24.97c-59.46,15.93 -120.58,-19.35 -136.51,-78.82l-18.52,-69.12c-6.81,-25.42 8.28,-51.56 33.7,-58.37l216.44,-58c25.42,-6.81 51.56,8.28 58.37,33.7l18.52,69.12c15.93,59.46 -19.35,120.58 -78.82,136.51zM255.69,692.33l-1.04,-0.28c-12.36,-3.27 -19.67,-15.89 -16.33,-28.19l13.54,-49.96c3.33,-12.3 16.05,-19.62 28.41,-16.35l1.04,0.28c12.36,3.27 19.67,15.89 16.33,28.19l-13.54,49.96c-3.33,12.3 -16.05,19.62 -28.41,16.35zM436.6,634.7l-36.62,-36.73c-9.01,-9.04 -9.03,-23.69 -0.04,-32.71l0.76,-0.76c9,-9.02 23.6,-9.01 32.61,0.04l36.62,36.73c9.01,9.04 9.03,23.69 0.04,32.71l-0.76,0.76c-9,9.02 -23.6,9.01 -32.61,-0.04z"
android:fillColor="#41eb8e" />
<path
android:pathData="m229.33,167.8c88.82,-23.8 180.11,28.91 203.91,117.73l1.04,3.87c10.01,37.34 -12.15,75.73 -49.5,85.73l-186.4,49.95c-37.34,10.01 -75.73,-12.15 -85.73,-49.5l-1.04,-3.87c-23.8,-88.82 28.91,-180.11 117.73,-203.91zM335.69,116.4l1.09,0.29c12.88,3.41 20.51,16.57 17.03,29.39l-14.12,52.08c-3.48,12.82 -16.74,20.45 -29.62,17.04l-1.09,-0.29c-12.88,-3.41 -20.51,-16.57 -17.03,-29.39l14.12,-52.08c3.48,-12.82 16.74,-20.45 29.62,-17.04zM147.08,176.49l38.17,38.29c9.4,9.43 9.41,24.7 0.04,34.1l-0.79,0.79c-9.38,9.41 -24.6,9.39 -34,-0.04l-38.17,-38.29c-9.4,-9.43 -9.41,-24.7 -0.04,-34.1l0.79,-0.79c9.38,-9.41 24.6,-9.39 34,0.04z"
android:fillColor="#41eb8e" />
<path
android:pathData="m171.1,170.45c14.47,-6.06 31.14,0.64 37.24,14.97c6.09,14.33 -0.7,30.87 -15.17,36.93c-14.47,6.06 -31.14,-0.64 -37.24,-14.97c-6.09,-14.33 0.7,-30.87 15.17,-36.93zM155.24,121.04l21.83,-9.15l32.8,77.13l-21.83,9.15l-32.8,-77.13zM159.84,131.85l-9.2,-21.63l38.57,-16.16l9.2,21.63l-38.57,16.16zM233.88,104.24c8.87,0.47 15.66,7.98 15.17,16.76c-0.49,8.79 -8.07,15.53 -16.94,15.06c-8.87,-0.47 -15.66,-7.98 -15.17,-16.76c0.49,-8.79 8.07,-15.53 16.94,-15.06zM238.18,75.21l13.38,0.71l-2.63,47.3l-13.38,-0.71l2.63,-47.3zM237.81,81.84l0.74,-13.26l23.64,1.25l-0.74,13.26l-23.64,-1.25z"
android:fillColor="#ffcf3b" />
<path
android:pathData="m199.55,259.53c14.4,-3.86 29.2,4.67 33.06,19.06c3.85,14.38 -4.7,29.17 -19.1,33.03c-14.4,3.86 -29.2,-4.67 -33.06,-19.06c-3.85,-14.38 4.7,-29.17 19.1,-33.03z"
android:strokeAlpha="0.8"
android:fillColor="#1f6a50"
android:fillAlpha="0.8" />
<path
android:pathData="m301.94,232.09c14.4,-3.86 29.2,4.67 33.06,19.06c3.85,14.38 -4.7,29.17 -19.1,33.03c-14.4,3.86 -29.2,-4.67 -33.06,-19.06c-3.85,-14.38 4.7,-29.17 19.1,-33.03z"
android:strokeAlpha="0.8"
android:fillColor="#1f6a50"
android:fillAlpha="0.8" />
<path
android:pathData="m281.58,328.54c26.25,15.27 30.16,38.74 18.87,59.03l0.01,0.01l-40.26,70.19c-2.22,3.83 -7.1,5.13 -10.91,2.92l-12.35,-7.18c-3.81,-2.21 -5.1,-7.11 -2.88,-10.93l20.08,-35.15c-4.89,-1.04 -9.74,-2.87 -14.34,-5.55c-14.36,-8.35 -22.78,-22.8 -23.43,-37.43l21.17,-36.56c13.47,-6.97 30.83,-7.03 44.05,0.66z"
android:fillColor="#f9f9f9"
android:fillType="evenOdd" />
<path
android:pathData="m248.73,352.49c2.77,1.61 3.7,5.18 2.08,7.96c-1.62,2.79 -5.17,3.75 -7.93,2.14c-2.77,-1.61 -3.7,-5.18 -2.08,-7.96c1.62,-2.79 5.17,-3.75 7.93,-2.14z"
android:fillColor="#272727" />
<path
android:pathData="m131.01,300.65c-2.65,4.63 -5.93,8.64 -9.64,11.98l35.02,20.46c3.84,2.21 5.17,7.1 2.98,10.93l-7.1,12.39c-2.19,3.82 -7.08,5.13 -10.91,2.92l-70.08,-40.76l0.01,-0.01c-19.95,-11.96 -28.34,-34.28 -13.25,-60.64c7.6,-13.27 22.66,-21.9 37.83,-22.58l36.67,21.14c6.77,13.02 6.72,29.75 -1.54,44.17z"
android:fillColor="#f9f9f9"
android:fillType="evenOdd" />
<path
android:pathData="m106.46,260.17c2.8,1.61 3.78,5.17 2.19,7.95c-1.59,2.78 -5.15,3.72 -7.95,2.11c-2.8,-1.61 -3.78,-5.17 -2.19,-7.95c1.59,-2.78 5.15,-3.72 7.95,-2.11z"
android:fillColor="#272727" />
</vector>
@@ -1,10 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="40dp"
android:drawable="@drawable/splash_graphic"
android:left="40dp"
android:right="40dp"
android:top="40dp" />
android:width="48dp"
android:height="48dp"
android:drawable="@mipmap/ic_launcher"
android:gravity="center" />
</layer-list>
@@ -1,34 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/permission_label"
android:id="@+id/user_time"
style="@style/UserTimeStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="12:23" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="match_parent"
android:layout_marginTop="32dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/overview_nomaindevice_label"
app:layout_constraintBottom_toTopOf="@id/permission_description"
app:layout_constraintBottom_toTopOf="@id/body"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/permission_description"
android:id="@+id/body"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="32dp"
android:text="@string/overview_nomaindevice_description"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/permission_label" />
app:layout_constraintTop_toBottomOf="@id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -7,14 +7,23 @@
android:layout_margin="8dp"
tools:context=".wear.ui.MainActivity">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/user_time"
style="@style/UserTimeStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="12:23" />
<ImageView
android:id="@+id/device_icon"
android:layout_width="16dp"
android:layout_height="16dp"
app:layout_constraintBottom_toTopOf="@+id/name"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginBottom="2dp"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/devic_airpods_gen1_both" />
@@ -36,15 +45,15 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:gravity="center"
app:lineHeight="14sp"
android:layout_marginBottom="4dp"
android:gravity="center"
android:lines="2"
android:maxLines="2"
app:layout_constraintBottom_toTopOf="@id/barrier_top"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/name"
app:lineHeight="14sp"
tools:text="Music Active, Case Closed" />
<androidx.constraintlayout.widget.Barrier
@@ -267,7 +276,7 @@
<com.google.android.material.textview.MaterialTextView
android:id="@+id/last_seen"
style="@style/TextAppearance.Material3.BodySmall"
style="@style/TextAppearance.Material3.LabelSmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
@@ -7,10 +7,21 @@
android:layout_margin="8dp"
tools:context=".wear.ui.MainActivity">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/user_time"
style="@style/UserTimeStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="12:23" />
<ImageView
android:id="@+id/device_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_width="16dp"
android:layout_height="16dp"
android:visibility="invisible"
app:layout_constraintBottom_toTopOf="@+id/name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
+6
View File
@@ -15,4 +15,10 @@
<item name="android:gravity">center</item>
<item name="android:ellipsize">end</item>
</style>
<style name="UserTimeStyle" parent="TextAppearance.Material3.LabelMedium">
<item name="android:singleLine">true</item>
<item name="android:gravity">center</item>
<item name="android:ellipsize">marquee</item>
</style>
</resources>
+3 -2
View File
@@ -29,9 +29,10 @@
<item name="colorPrimaryInverse">@color/md_theme_dark_primaryInverse</item>
</style>
<style name="AppThemeSplash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">#3f7aff</item>
<style name="AppThemeSplash" parent="Theme.SplashScreen.IconBackground">
<item name="windowSplashScreenBackground">#000000</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_screen</item>
<item name="windowSplashScreenIconBackgroundColor">#000000</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
+6 -10
View File
@@ -8,8 +8,8 @@ apply(plugin = "dagger.hilt.android.plugin")
apply(plugin = "androidx.navigation.safeargs.kotlin")
android {
compileSdk = ProjectConfig.compileSdk
namespace = "${ProjectConfig.packageName}"
defaultConfig {
applicationId = ProjectConfig.packageName
@@ -42,12 +42,9 @@ android {
create("gplay") {
dimension = "version"
signingConfig = signingConfigs["releaseGplay"]
extra["useBugsnag"] = true
}
}
setupBugsnagPlugin()
buildTypes {
val customProguardRules = fileTree(File(projectDir, "proguard")) {
include("*.pro")
@@ -100,12 +97,12 @@ android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
freeCompilerArgs = freeCompilerArgs + listOf(
"-opt-in=kotlin.ExperimentalStdlibApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
@@ -141,6 +138,8 @@ dependencies {
addBaseAndroid()
addBaseAndroidUi()
implementation("androidx.core:core-splashscreen:1.0.0-alpha02")
addNavigation()
addBaseWorkManager()
@@ -148,7 +147,4 @@ dependencies {
"gplayImplementation"("com.android.billingclient:billing:5.1.0")
"gplayImplementation"("com.android.billingclient:billing-ktx:5.1.0")
"gplayImplementation"("com.bugsnag:bugsnag-android:5.9.2")
"gplayImplementation"("com.getkeepsafe.relinker:relinker:1.4.3")
}
@@ -32,7 +32,7 @@ class UpgradeControlFoss @Inject constructor(
override fun launchBillingFlow(activity: Activity) {
MaterialAlertDialogBuilder(activity).apply {
setIcon(R.drawable.ic_heart)
setIcon(eu.darken.capod.common.R.drawable.ic_heart)
setTitle(R.string.upgrade_capod_label)
setMessage(R.string.upgrade_capod_description)
setPositiveButton(R.string.foss_upgrade_donate_label) { _, _ ->
@@ -68,6 +68,6 @@ class UpgradeControlFoss @Inject constructor(
override val type: UpgradeRepo.Type = UpgradeRepo.Type.FOSS
}
override fun getSponsorUrl(): String? = "https://github.com/sponsors/d4rken"
override fun getSponsorUrl(): String = "https://github.com/sponsors/d4rken"
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="foss_upgrade_donate_label">दान करना</string>
<string name="foss_upgrade_donate_label">दान करें</string>
<string name="foss_upgrade_alreadydonated_label">मैंने पहले ही दान कर दिया है</string>
<string name="foss_upgrade_no_money_label">मैं अपना सारा पैसा AirPods पर खर्च करता हूं</string>
</resources>
+3 -3
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="foss_upgrade_donate_label">Bağış</string>
<string name="foss_upgrade_alreadydonated_label">Ben zaten bağışladım</string>
<string name="foss_upgrade_no_money_label">Tüm paramı AirPod\'lara harcıyorum</string>
<string name="foss_upgrade_donate_label">Bağış yap</string>
<string name="foss_upgrade_alreadydonated_label">Zaten bağış yaptım</string>
<string name="foss_upgrade_no_money_label">Tüm paramı AirPods\'a harcıyorum</string>
</resources>
+5 -1
View File
@@ -1,2 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
<resources>
<string name="foss_upgrade_donate_label">資助</string>
<string name="foss_upgrade_alreadydonated_label">我已經資助了</string>
<string name="foss_upgrade_no_money_label">我已經為 AirPods 傾家蕩產了</string>
</resources>
@@ -73,7 +73,7 @@ class UpgradeRepoGplay @Inject constructor(
override fun launchBillingFlow(activity: Activity) {
MaterialAlertDialogBuilder(activity).apply {
setIcon(R.drawable.ic_heart)
setIcon(eu.darken.capod.common.R.drawable.ic_heart)
setTitle(R.string.upgrade_capod_label)
setMessage(R.string.upgrade_capod_description)
setPositiveButton(R.string.general_upgrade_action) { _, _ ->
+1 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="upgrades_gplay_unavailable_error">Google Play सेवाएं अनुपलब्ध हैं।</string>
<string name="upgrades_no_purchases_found_check_account">कोई खरीदारी नहीं मिली. क्या आप सही खाते का उपयोग कर रहे हैं?</string>
<string name="upgrades_no_purchases_found_check_account">कोई खरीदारी नहीं मिली, क्या आप सही खाते का उपयोग कर रहे हैं?</string>
</resources>
+4 -1
View File
@@ -1,2 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>
<resources>
<string name="upgrades_gplay_unavailable_error">Google Play 服務無法使用。</string>
<string name="upgrades_no_purchases_found_check_account">未找到訂單,確定帳戶無誤?</string>
</resources>
@@ -6,7 +6,6 @@ import android.content.Context
import android.os.Handler
import android.os.Looper
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.R
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import java.util.concurrent.locks.ReentrantLock
@@ -41,7 +40,7 @@ class ClipboardHelper @Inject constructor(
}
fun copyToClipboard(text: String) {
val clip = ClipData.newPlainText(context.getString(R.string.app_name), text)
val clip = ClipData.newPlainText(context.getString(eu.darken.capod.common.R.string.app_name), text)
clipboard.setPrimaryClip(clip)
}
@@ -73,7 +73,7 @@ class RecorderService : Service2() {
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSmallIcon(R.drawable.ic_baseline_bug_report_24)
.setContentText("Idle")
.setContentTitle(getString(R.string.app_name))
.setContentTitle(getString(eu.darken.capod.common.R.string.app_name))
.addAction(NotificationCompat.Action.Builder(0, getString(R.string.general_done_action), stopPi).build())
startForeground(NOTIFICATION_ID, builder.build())
@@ -0,0 +1,42 @@
package eu.darken.capod.common.preferences
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.util.AttributeSet
import android.widget.Toast
import androidx.annotation.AttrRes
import androidx.annotation.StyleRes
import androidx.preference.Preference
import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
class IntentPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyleAttr: Int = androidx.preference.R.attr.preferenceStyle,
@StyleRes defStyleRes: Int = 0,
) : Preference(context, attrs, defStyleAttr, defStyleRes) {
override fun setIntent(_intent: Intent?) {
super.setIntent(_intent)
_intent?.let {
intent
setOnPreferenceClickListener {
try {
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
log(TAG, ERROR) { "Failed to launch $intent: $e" }
Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show()
}
true
}
}
}
companion object {
private val TAG = logTag("IntentPreference")
}
}
@@ -0,0 +1,29 @@
package eu.darken.capod.main.ui.onboarding
import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.PrivacyPolicy
import eu.darken.capod.common.WebpageTool
import eu.darken.capod.common.uix.Fragment3
import eu.darken.capod.common.viewbinding.viewBinding
import eu.darken.capod.databinding.OnboardingFragmentBinding
import javax.inject.Inject
@AndroidEntryPoint
class OnboardingFragment : Fragment3(R.layout.onboarding_fragment) {
override val vm: OnboardingFragmentVM by viewModels()
override val ui: OnboardingFragmentBinding by viewBinding()
@Inject lateinit var webpageTool: WebpageTool
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
ui.goPrivacyPolicy.setOnClickListener { webpageTool.open(PrivacyPolicy.URL) }
ui.continueAction.setOnClickListener { vm.finishOnboarding() }
super.onViewCreated(view, savedInstanceState)
}
}
@@ -0,0 +1,27 @@
package eu.darken.capod.main.ui.onboarding
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.uix.ViewModel3
import eu.darken.capod.main.core.GeneralSettings
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@HiltViewModel
class OnboardingFragmentVM @Inject constructor(
@Suppress("UNUSED_PARAMETER") handle: SavedStateHandle,
private val dispatcherProvider: DispatcherProvider,
private val generalSettings: GeneralSettings,
) : ViewModel3(dispatcherProvider = dispatcherProvider) {
fun finishOnboarding() = launch {
generalSettings.isOnboardingDone.value = true
OnboardingFragmentDirections.actionOnboardingFragmentToOverviewFragment().navigate()
}
companion object {
val TAG = logTag("Onboarding", "Fragment", "VM")
}
}
@@ -8,6 +8,8 @@ import android.text.SpannableStringBuilder
import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.BuildConfig
@@ -115,18 +117,18 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
val baseTitle = when (info.type) {
UpgradeRepo.Type.GPLAY -> {
if (info.isPro) {
getString(R.string.app_name_pro)
getString(eu.darken.capod.common.R.string.app_name_pro)
} else {
gplay.isVisible = true
getString(R.string.app_name)
getString(eu.darken.capod.common.R.string.app_name)
}
}
UpgradeRepo.Type.FOSS -> {
if (info.isPro) {
getString(R.string.app_name_foss)
getString(eu.darken.capod.common.R.string.app_name_foss)
} else {
donate.isVisible = true
getString(R.string.app_name)
getString(eu.darken.capod.common.R.string.app_name)
}
}
}.split(" ".toRegex())
@@ -141,12 +143,13 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
}
builder.append(colorString(requireContext(), color, baseTitle[1]))
} else {
getString(R.string.app_name)
getString(eu.darken.capod.common.R.string.app_name)
}
}
vm.launchUpgradeFlow.observe2 {
it(requireActivity())
}
super.onViewCreated(view, savedInstanceState)
}
@@ -11,7 +11,6 @@ import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.flow.combine
import eu.darken.capod.common.flow.throttleLatest
import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.navigation.navVia
import eu.darken.capod.common.permissions.Permission
import eu.darken.capod.common.uix.ViewModel3
import eu.darken.capod.common.upgrade.UpgradeRepo
@@ -48,6 +47,12 @@ class OverviewFragmentVM @Inject constructor(
private val bluetoothManager: BluetoothManager2,
) : ViewModel3(dispatcherProvider = dispatcherProvider) {
init {
if (!generalSettings.isOnboardingDone.value) {
OverviewFragmentDirections.actionOverviewFragmentToOnboardingFragment().navigate()
}
}
val upgradeState = upgradeRepo.upgradeInfo.asLiveData2()
val launchUpgradeFlow = SingleLiveEvent<(Activity) -> Unit>()
@@ -124,7 +129,7 @@ class OverviewFragmentVM @Inject constructor(
items.add(0, BluetoothDisabledVH.Item)
} else if (mainPod == null) {
items.add(0, MissingMainDeviceVH.Item {
OverviewFragmentDirections.actionOverviewFragmentToTroubleShooterFragment().navVia(this)
OverviewFragmentDirections.actionOverviewFragmentToTroubleShooterFragment().navigate()
})
}
}
@@ -140,12 +145,14 @@ class OverviewFragmentVM @Inject constructor(
showDebug = isDebugMode,
isMainPod = isMainPod,
)
is SinglePodDevice -> SinglePodsCardVH.Item(
now = now,
device = it,
showDebug = isDebugMode,
isMainPod = isMainPod,
)
else -> UnknownPodDeviceCardVH.Item(
now = now,
device = it,
@@ -166,7 +173,7 @@ class OverviewFragmentVM @Inject constructor(
}
fun goToSettings() = launch {
OverviewFragmentDirections.actionOverviewFragmentToSettingsFragment().navVia(this@OverviewFragmentVM)
OverviewFragmentDirections.actionOverviewFragmentToSettingsFragment().navigate()
}
fun onUpgrade() = launch {
@@ -7,9 +7,21 @@ import androidx.core.view.isInvisible
import eu.darken.capod.R
import eu.darken.capod.common.lists.binding
import eu.darken.capod.databinding.OverviewPodsDualItemBinding
import eu.darken.capod.pods.core.*
import eu.darken.capod.pods.core.DualPodDevice
import eu.darken.capod.pods.core.HasCase
import eu.darken.capod.pods.core.HasChargeDetectionDual
import eu.darken.capod.pods.core.HasDualMicrophone
import eu.darken.capod.pods.core.HasEarDetectionDual
import eu.darken.capod.pods.core.HasPodStyle
import eu.darken.capod.pods.core.HasStateDetection
import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.DualApplePods.LidState
import eu.darken.capod.pods.core.firstSeenFormatted
import eu.darken.capod.pods.core.getBatteryDrawable
import eu.darken.capod.pods.core.getBatteryLevelCase
import eu.darken.capod.pods.core.getBatteryLevelLeftPod
import eu.darken.capod.pods.core.getBatteryLevelRightPod
import eu.darken.capod.pods.core.lastSeenFormatted
import java.time.Duration
import java.time.Instant
@@ -42,8 +54,10 @@ class DualPodsCardVH(parent: ViewGroup) :
podLeftIcon.setImageResource(device.leftPodIcon)
podRightIcon.setImageResource(device.rightPodIcon)
lastSeen.text = context.getString(R.string.last_seen_x, device.lastSeenFormatted(item.now))
firstSeen.text = context.getString(R.string.first_seen_x, device.firstSeenFormatted(item.now))
lastSeen.text =
context.getString(eu.darken.capod.common.R.string.last_seen_x, device.lastSeenFormatted(item.now))
firstSeen.text =
context.getString(eu.darken.capod.common.R.string.first_seen_x, device.firstSeenFormatted(item.now))
firstSeen.isGone = Duration.between(device.seenFirstAt, device.seenLastAt).toMinutes() < 1
reception.text = item.getReceptionText()
@@ -131,9 +145,9 @@ class DualPodsCardVH(parent: ViewGroup) :
device.apply {
if (this is DualApplePods) {
podCaseLidLabel.text = when (caseLidState) {
LidState.OPEN -> context.getString(R.string.pods_case_status_open_label)
LidState.CLOSED -> context.getString(R.string.pods_case_status_closed_label)
else -> context.getString(R.string.pods_case_unknown_state)
LidState.OPEN -> context.getString(eu.darken.capod.common.R.string.pods_case_status_open_label)
LidState.CLOSED -> context.getString(eu.darken.capod.common.R.string.pods_case_status_closed_label)
else -> context.getString(eu.darken.capod.common.R.string.pods_case_unknown_state)
}
val hideInfo = !listOf(LidState.OPEN, LidState.CLOSED).contains(caseLidState)
@@ -3,7 +3,6 @@ package eu.darken.capod.main.ui.overview.cards.pods
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.viewbinding.ViewBinding
import eu.darken.capod.R
import eu.darken.capod.common.lists.BindableVH
import eu.darken.capod.common.lists.differ.DifferItem
import eu.darken.capod.common.lists.modular.ModularAdapter
@@ -19,7 +18,7 @@ abstract class PodDeviceVH<D : PodDeviceVH.Item, B : ViewBinding>(
fun Item.getReceptionText(): String = device.getSignalQuality(context)
.let { if (showDebug) "$it ${device.seenCounter}" else it }
.let { if (isMainPod) "$it\n(${getString(R.string.pods_yours)})" else it }
.let { if (isMainPod) "$it\n(${getString(eu.darken.capod.common.R.string.pods_yours)})" else it }
interface Item : OverviewAdapter.Item {
@@ -32,8 +32,8 @@ class UnknownPodDeviceCardVH(parent: ViewGroup) :
reception.text = item.getReceptionText()
details.text = when (item.device) {
is ApplePods -> getString(R.string.pods_unknown_contact_dev)
else -> getString(R.string.pods_unknown_label)
is ApplePods -> getString(eu.darken.capod.common.R.string.pods_unknown_contact_dev)
else -> getString(eu.darken.capod.common.R.string.pods_unknown_label)
}
rawdata.text = device.rawDataHex.joinToString("\n")
@@ -2,7 +2,10 @@ package eu.darken.capod.main.ui.settings
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ListView
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.viewModels
import androidx.preference.Preference
@@ -15,6 +18,7 @@ import eu.darken.capod.common.viewbinding.viewBinding
import eu.darken.capod.databinding.SettingsFragmentBinding
import kotlinx.parcelize.Parcelize
@AndroidEntryPoint
class SettingsFragment : Fragment2(R.layout.settings_fragment),
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
@@ -1,19 +1,19 @@
package eu.darken.capod.main.ui.settings.general
import android.bluetooth.BluetoothDevice
import android.content.Context
import android.content.DialogInterface
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.darken.capod.R
import eu.darken.capod.common.bluetooth.BluetoothDevice2
class DeviceSelectionDialogFactory constructor(private val context: Context) {
fun create(
devices: List<BluetoothDevice>,
current: BluetoothDevice?,
callback: (BluetoothDevice?) -> Unit
devices: List<BluetoothDevice2>,
current: BluetoothDevice2?,
callback: (BluetoothDevice2?) -> Unit
): AlertDialog {
return MaterialAlertDialogBuilder(context).apply {
setTitle(R.string.settings_maindevice_address_label)
@@ -30,7 +30,6 @@ class GeneralSettingsFragmentVM @Inject constructor(
val events = SingleLiveEvent<GeneralSettingsEvents>()
init {
generalSettings.monitorMode.flow
.withPrevious()
.filter { (old, new) ->
@@ -21,7 +21,19 @@ import eu.darken.capod.common.upgrade.isPro
import eu.darken.capod.main.ui.MainActivity
import eu.darken.capod.monitor.core.PodDeviceCache
import eu.darken.capod.monitor.core.PodMonitor
import eu.darken.capod.pods.core.*
import eu.darken.capod.pods.core.DualPodDevice
import eu.darken.capod.pods.core.HasCase
import eu.darken.capod.pods.core.HasChargeDetectionDual
import eu.darken.capod.pods.core.HasEarDetection
import eu.darken.capod.pods.core.HasEarDetectionDual
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.PodFactory
import eu.darken.capod.pods.core.SinglePodDevice
import eu.darken.capod.pods.core.getBatteryDrawable
import eu.darken.capod.pods.core.getBatteryLevelCase
import eu.darken.capod.pods.core.getBatteryLevelHeadset
import eu.darken.capod.pods.core.getBatteryLevelLeftPod
import eu.darken.capod.pods.core.getBatteryLevelRightPod
import finish2
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -135,7 +147,7 @@ class WidgetProvider : AppWidgetProvider() {
setOnClickPendingIntent(R.id.widget_root, pendingIntent)
setTextViewText(R.id.primary, context.getString(R.string.pods_unknown_label))
setTextViewText(R.id.primary, context.getString(eu.darken.capod.common.R.string.pods_unknown_label))
}
private fun createNoDeviceLayout(
@@ -151,7 +163,7 @@ class WidgetProvider : AppWidgetProvider() {
setOnClickPendingIntent(R.id.widget_root, pendingIntent)
setTextViewText(R.id.primary, context.getString(R.string.overview_nomaindevice_label))
setTextViewText(R.id.primary, context.getString(eu.darken.capod.common.R.string.overview_nomaindevice_label))
}
private fun createDualPodLayout(
@@ -1,7 +1,6 @@
package eu.darken.capod.monitor.core.worker
import android.app.NotificationManager
import android.bluetooth.BluetoothDevice
import android.content.Context
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
@@ -9,6 +8,7 @@ import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import eu.darken.capod.common.bluetooth.BluetoothDevice2
import eu.darken.capod.common.bluetooth.BluetoothManager2
import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.Bugs
@@ -30,8 +30,20 @@ import eu.darken.capod.reaction.core.autoconnect.AutoConnect
import eu.darken.capod.reaction.core.playpause.PlayPause
import eu.darken.capod.reaction.core.popup.PopUpReaction
import eu.darken.capod.reaction.ui.popup.PopUpWindow
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
@HiltWorker
@@ -130,7 +142,7 @@ class MonitorWorker @AssistedInject constructor(
.flatMapLatest { arguments ->
val monitorMode = arguments[0] as MonitorMode
val mainAddress = arguments[1] as String?
val devices = arguments[2] as Collection<BluetoothDevice>
val devices = arguments[2] as Collection<BluetoothDevice2>
log(TAG) { "Monitor mode: $monitorMode" }
when (monitorMode) {
@@ -145,7 +157,7 @@ class MonitorWorker @AssistedInject constructor(
log(TAG, WARN) { "Main device address not set, staying alive while any is connected" }
}
devices.any { it.address == mainAddress } -> {
log(TAG) { "MainDevice is connected ($mainAddress), aborting any timeout." }
log(TAG) { "Main device is connected ($mainAddress), aborting any timeout." }
}
else -> {
log(TAG) { "No known Pods are connected, canceling worker soon." }
@@ -53,9 +53,9 @@ class MonitorNotifications @Inject constructor(
setChannelId(NOTIFICATION_CHANNEL_ID)
setContentIntent(openPi)
priority = NotificationCompat.PRIORITY_LOW
setSmallIcon(R.drawable.devic_earbuds_generic_both)
setSmallIcon(eu.darken.capod.common.R.drawable.devic_earbuds_generic_both)
setOngoing(true)
setContentTitle(context.getString(R.string.app_name))
setContentTitle(context.getString(eu.darken.capod.common.R.string.app_name))
}
}
@@ -64,9 +64,9 @@ class MonitorNotifications @Inject constructor(
return builder.apply {
setCustomContentView(null)
setStyle(NotificationCompat.BigTextStyle())
setContentTitle(context.getString(R.string.pods_none_label_short))
setSubText(context.getString(R.string.app_name))
setSmallIcon(R.drawable.devic_earbuds_generic_both)
setContentTitle(context.getString(eu.darken.capod.common.R.string.pods_none_label_short))
setSubText(context.getString(eu.darken.capod.common.R.string.app_name))
setSmallIcon(eu.darken.capod.common.R.drawable.devic_earbuds_generic_both)
}
}
@@ -1,6 +1,5 @@
package eu.darken.capod.reaction.ui
import android.bluetooth.BluetoothDevice
import android.os.Bundle
import android.view.View
import androidx.annotation.Keep
@@ -11,6 +10,7 @@ import androidx.preference.ListPreference
import androidx.preference.Preference
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.capod.R
import eu.darken.capod.common.bluetooth.BluetoothDevice2
import eu.darken.capod.common.uix.PreferenceFragment3
import eu.darken.capod.common.upgrade.UpgradeRepo
import eu.darken.capod.main.core.GeneralSettings
@@ -36,7 +36,7 @@ class ReactionSettingsFragment : PreferenceFragment3() {
override val preferenceFile: Int = R.xml.preferences_reactions
private var isPro: Boolean = false
private var bondedDevices: List<BluetoothDevice> = emptyList()
private var bondedDevices: List<BluetoothDevice2> = emptyList()
private val autoConnectConditionPref by lazy { findPreference<ListPreference>(settings.autoConnectCondition.key)!! }
override fun onPreferencesCreated() {
@@ -83,6 +83,11 @@ class ReactionSettingsFragment : PreferenceFragment3() {
upgradeRepo.launchBillingFlow(requireActivity())
preference.isChecked = false
return true
} else if (preference.key == reactionSettings.showPopUpOnConnection.key && !isPro) {
preference as CheckBoxPreference
upgradeRepo.launchBillingFlow(requireActivity())
preference.isChecked = false
return true
}
return super.onPreferenceTreeClick(preference)
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20,2H4A2,2 0 0,0 2,4V22L6,18H20A2,2 0 0,0 22,16V4C22,2.89 21.1,2 20,2Z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20 2H4C2.9 2 2 2.9 2 4V22L6 18H20C21.1 18 22 17.1 22 16V4C22 2.9 21.1 2 20 2M20 16H5.2L4 17.2V4H20V16Z" />
</vector>
@@ -22,6 +22,8 @@
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:paddingBottom="48dp"
android:clipToPadding="false"
tools:listitem="@layout/overview_pods_dual_item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="32dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/mascot"
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_marginTop="48dp"
android:src="@drawable/splash_graphic2"
app:layout_constraintEnd_toEndOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.HeadlineLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/app_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/mascot" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/body1"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/onboarding_body1"
app:layout_constraintTop_toBottomOf="@id/title" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/body2"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/onboarding_body2"
app:layout_constraintTop_toBottomOf="@id/body1" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/body3"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/onboarding_body3"
app:layout_constraintTop_toBottomOf="@id/body2" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/body4"
style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/onboarding_body4"
app:layout_constraintTop_toBottomOf="@id/body3" />
<com.google.android.material.button.MaterialButton
android:id="@+id/go_privacy_policy"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="32dp"
android:text="@string/settings_privacy_policy_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/body4" />
<com.google.android.material.button.MaterialButton
android:id="@+id/continue_action"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="32dp"
android:text="@string/general_continue_action"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/go_privacy_policy" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
@@ -4,17 +4,6 @@
style="@style/PodWidget.Container"
android:orientation="vertical">
<TextView
android:id="@+id/headphones_label"
style="@style/PodWidget.TextPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="2dp"
android:textSize="12sp"
android:textStyle="bold"
tools:text="AirPods Max" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -110,4 +99,14 @@
android:src="@drawable/ic_baseline_power_24" />
</LinearLayout>
<TextView
android:id="@+id/headphones_label"
style="@style/PodWidget.TextPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="12sp"
android:textStyle="bold"
tools:text="AirPods Max" />
</LinearLayout>
+11
View File
@@ -16,6 +16,9 @@
<action
android:id="@+id/action_overviewFragment_to_troubleShooterFragment"
app:destination="@id/troubleShooterFragment" />
<action
android:id="@+id/action_overviewFragment_to_onboardingFragment"
app:destination="@id/onboardingFragment" />
</fragment>
<fragment
@@ -27,5 +30,13 @@
android:id="@+id/troubleShooterFragment"
android:name="eu.darken.capod.troubleshooter.ui.TroubleShooterFragment"
tools:layout="@layout/troubleshooter_fragment" />
<fragment
android:id="@+id/onboardingFragment"
android:name="eu.darken.capod.main.ui.onboarding.OnboardingFragment"
tools:layout="@layout/onboarding_fragment">
<action
android:id="@+id/action_onboardingFragment_to_overviewFragment"
app:destination="@id/overviewFragment" />
</fragment>
</navigation>
+1 -2
View File
@@ -37,10 +37,9 @@
<string name="settings_maindevice_address_none">غير موجود</string>
<string name="settings_maindevice_model_label">طِراز جهازك</string>
<string name="settings_maindevice_model_description">طِراز جهازك الرئيسي. يساعد هذا التطبيق في التعرف على جهازك عندما لا يكون متصلاً بهاتفك.</string>
<string name="settings_popup_caseopen_label">عرض إشعار منبثق</string>
<string name="settings_popup_caseopen_description">عرض إشعار منبثق عند فتح علبة الجهاز (تجريبي).</string>
<string name="settings_onepod_mode_label">وضعية السماعة الواحدة</string>
<string name="settings_onepod_mode_description">ليس بالضرورة ارتداء كِلتا السمَّاعتين، ارتداء سمَّاعة واحدة يكفي لتحريك ردود الفعل.</string>
<string name="settings_popup_caseopen_description">عرض إشعار منبثق عند فتح علبة الجهاز (تجريبي).</string>
<string name="notification_channel_device_status_label">حالة الجهاز</string>
<string name="debug_debuglog_size_label">الحجم</string>
<string name="debug_debuglog_size_compressed_label">الحجم المضغوط</string>
+1 -2
View File
@@ -33,10 +33,9 @@
<string name="settings_maindevice_address_none">Heç biri</string>
<string name="settings_maindevice_model_label">Cihazınızın modeli</string>
<string name="settings_maindevice_model_description">Əsas cihazının modeli. Bu, telefonunuzla bağlantı qurulmadığı vaxtlarda tətbiqin cihazınızı tanımasına kömək edir.</string>
<string name="settings_popup_caseopen_label">Açılan pəncərəni göstər</string>
<string name="settings_popup_caseopen_description">Cihazın qutusu açılanda bir açılan pəncərə göstər (təcrübi).</string>
<string name="settings_onepod_mode_label">Tək tərəf rejimi</string>
<string name="settings_onepod_mode_description">Hər iki tərəfi də taxmağa ehtiyac yoxdur, reaksiyaları tətikləmək üçün tək tərəfi taxmaq yetərlidir.</string>
<string name="settings_popup_caseopen_description">Cihazın qutusu açılanda bir açılan pəncərə göstər (təcrübi).</string>
<string name="notification_channel_device_status_label">Cihaz vəziyyəti</string>
<string name="debug_debuglog_size_label">Həcm</string>
<string name="debug_debuglog_size_compressed_label">Sıxışdırılmış həcm</string>
+4 -3
View File
@@ -43,10 +43,9 @@
<string name="settings_maindevice_address_none">Няма</string>
<string name="settings_maindevice_model_label">Мадэль вашай прылады</string>
<string name="settings_maindevice_model_description">Мадэль вашай прылады. Гэта дапамагае праграме апазнаць вашу прыладу, калі яна не падключана да вашага тэлефона.</string>
<string name="settings_popup_caseopen_label">Паказваць апавяшчэнне</string>
<string name="settings_popup_caseopen_description">Паказваць апавяшчэнне, калі футляр прылады адчынены (эксперыментальная функцыя).</string>
<string name="settings_onepod_mode_label">Рэжым аднаго навушніка</string>
<string name="settings_onepod_mode_description">Нашэнне абодвух навушнікаў не абавязкова, нашэнне аднаго навушніка цалкам дастаткова для запуску рэакцый.</string>
<string name="settings_popup_caseopen_description">Паказваць апавяшчэнне, калі футляр прылады адчынены (эксперыментальная функцыя).</string>
<string name="notification_channel_device_status_label">Статус прылады</string>
<string name="debug_debuglog_size_label">Памер</string>
<string name="debug_debuglog_size_compressed_label">Сціснуты памер</string>
@@ -95,5 +94,7 @@
<string name="troubleshooter_ble_result_success_body">Трансляцыі даных BLE прымаюцца CAPod.</string>
<string name="troubleshooter_ble_result_failure_title">Безвынікова</string>
<string name="troubleshooter_ble_result_failure_body">Не ўдалося выправіць непаладку. Не дапамагла ніводная камбінацыя параметраў сумяшчальнасці.</string>
<string name="troubleshooter_ble_result_failure_phone_body">Ваш тэлефон наогул не атрымаў ніякіх даных BLE. Вы можаце паўтарыць гэты тэст у шматлюдным месцы, каб даведацца, ці могуць быць атрыманыя даныя з іншых крыніц (акрамя вашых навушнікаў). Няма атрыманых даных, якія паказваюць на праблему з аперацыйнай сістэмай вашага тэлефона.</string>
<string name="troubleshooter_ble_result_failure_phone_headphones">Ваш тэлефон атрымаў даныя BLE, але яны паступаюць з прылады, якая не падтрымліваецца. Вашы навушнікі ўключаны? Ці падтрымлівае CAPod вашы навушнікі?</string>
<string name="troubleshooter_ble_result_failure_action">Паспрабаваць яшчэ раз</string>
<string name="troubleshoot_action">Выпраўленне непаладак</string>
</resources>
+5 -3
View File
@@ -43,10 +43,12 @@
<string name="settings_maindevice_address_none">Cap</string>
<string name="settings_maindevice_model_label">El vostre model de dispositiu</string>
<string name="settings_maindevice_model_description">El model del vostre dispositiu principal. Això ajuda l\'aplicació a reconèixer el vostre dispositiu quan no està connectat al vostre telèfon.</string>
<string name="settings_popup_caseopen_label">Mostra la finestra emergent</string>
<string name="settings_popup_caseopen_description">Mostra una finestra emergent quan s\'obre la funda del dispositiu (experimental).</string>
<string name="settings_onepod_mode_label">Mode d\'un AirPod</string>
<string name="settings_onepod_mode_description">No cal portar els dos AirdPods, per provocar reaccions només cal portar-ne un.</string>
<string name="settings_popup_caseopen_label">Mostra la finestra emergent de la funda</string>
<string name="settings_popup_caseopen_description">Mostra una finestra emergent quan s\'obre la funda del dispositiu (experimental).</string>
<string name="settings_popup_connected_label">Mostra la finestra emergent de connexió</string>
<string name="settings_popup_connected_description">Mostra una finestra emergent quan el dispositiu es connecti per primera vegada.</string>
<string name="notification_channel_device_status_label">Estat del dispositiu</string>
<string name="debug_debuglog_size_label">Mida</string>
<string name="debug_debuglog_size_compressed_label">Mida comprimida</string>
@@ -95,7 +97,7 @@
<string name="troubleshooter_ble_result_success_body">El CAPod està rebent emissions d\'anuncis BLE.</string>
<string name="troubleshooter_ble_result_failure_title">Incorrecte</string>
<string name="troubleshooter_ble_result_failure_body">La resolució de problemes ha fallat. No ha funcionat cap combinació d\'opcions de compatibilitat.</string>
<string name="troubleshooter_ble_result_failure_phone_body">El vostre telèfon no ha rebut cap dada BLE. Podeu tornar a provar aquesta prova en una zona plena de gent per veure si es poden rebre fonts de dades (que no siguin els vostres auriculars). No es reben dades que indiquen un problema amb el sistema operatiu del vostre telèfon.</string>
<string name="troubleshooter_ble_result_failure_phone_body">El vostres telèfon no ha rebut cap dada BLE. Podeu tornar a provar aquesta prova en una zona plena de gent per veure si es poden rebre fonts de dades (que no siguin els vostres auriculars). No rebre dades indica un problema amb el sistema operatiu del telèfon.</string>
<string name="troubleshooter_ble_result_failure_phone_headphones">El vostre telèfon ha rebut dades BLE, però les dades no provenen de cap dispositiu compatible. Teniu els auriculars encesos? El CAPod és compatible amb els vostres auriculars?</string>
<string name="troubleshooter_ble_result_failure_action">Torna-ho a provar</string>
<string name="troubleshoot_action">Resolució de problemes</string>
+4 -2
View File
@@ -43,10 +43,12 @@
<string name="settings_maindevice_address_none">Žádná</string>
<string name="settings_maindevice_model_label">Model vašeho zařízení</string>
<string name="settings_maindevice_model_description">Model vašeho hlavního zařízení. To pomůže aplikaci rozpoznat vaše zařízení, když není připojeno k telefonu.</string>
<string name="settings_popup_caseopen_label">Zobrazit vyskakovací okno</string>
<string name="settings_popup_caseopen_description">Zobrazit vyskakovací okno, pokud je pouzdro zařízení otevřeno (experimentální).</string>
<string name="settings_onepod_mode_label">Režim jednoho sluchátka</string>
<string name="settings_onepod_mode_description">Není nutné nosit obě sluchátka, k vyvolání reakce stačí nosit jen jedno.</string>
<string name="settings_popup_caseopen_label">Zobrazit vyskakovací okno pouzdra</string>
<string name="settings_popup_caseopen_description">Zobrazit vyskakovací okno, pokud je pouzdro zařízení otevřeno (experimentální).</string>
<string name="settings_popup_connected_label">Zobrazit vyskakovací okno při připojení</string>
<string name="settings_popup_connected_description">Zobrazit vyskakovací okno při prvním připojení zařízení.</string>
<string name="notification_channel_device_status_label">Stav zařízení</string>
<string name="debug_debuglog_size_label">Velikost</string>
<string name="debug_debuglog_size_compressed_label">Komprimovaná velikost</string>
+5 -3
View File
@@ -43,10 +43,12 @@
<string name="settings_maindevice_address_none">Keine</string>
<string name="settings_maindevice_model_label">Ihr Gerätemodell</string>
<string name="settings_maindevice_model_description">Das Modell Ihres Hauptgeräts. Dies hilft der App, Ihr Gerät zu erkennen, wenn es nicht mit Ihrem Telefon verbunden ist.</string>
<string name="settings_popup_caseopen_label">Popup zeigen</string>
<string name="settings_popup_caseopen_description">Ein Popup anzeigen, wenn das Gerätegehäuse geöffnet wird (experimentell).</string>
<string name="settings_onepod_mode_label">Ein-Pod-Modus</string>
<string name="settings_onepod_mode_description">Das Tragen beider Pods ist nicht erforderlich. Das Tragen eines einzigen Pods reicht aus, um Reaktionen auszulösen.</string>
<string name="settings_popup_caseopen_label">Hüllen popup anzeigen</string>
<string name="settings_popup_caseopen_description">Ein Popup anzeigen, wenn das Gerätegehäuse geöffnet wird (experimentell).</string>
<string name="settings_popup_connected_label">Verbindungs-Popup anzeigen</string>
<string name="settings_popup_connected_description">Zeigt ein Popup an, wenn das Gerät zum ersten Mal eine Verbindung herstellt.</string>
<string name="notification_channel_device_status_label">Geräte Status</string>
<string name="debug_debuglog_size_label">Größe</string>
<string name="debug_debuglog_size_compressed_label">Komprimierte Größe</string>
@@ -95,7 +97,7 @@
<string name="troubleshooter_ble_result_success_body">BLE-Werbesendungen werden von CAPod empfangen.</string>
<string name="troubleshooter_ble_result_failure_title">Erfolglos</string>
<string name="troubleshooter_ble_result_failure_body">Fehlerbehebung fehlgeschlagen. Keine Kombination von Kompatibilitätsoptionen hat geholfen.</string>
<string name="troubleshooter_ble_result_failure_phone_body">Ihr Telefon hat überhaupt keine BLE-Daten empfangen. Sie können diesen Test in einem überfüllten Bereich wiederholen, um zu sehen, ob Datenquellen (außer Ihren Kopfhörern) empfangen werden können. Es werden keine Daten empfangen, was auf ein Problem mit dem Betriebssystem Ihres Telefons hindeutet.</string>
<string name="troubleshooter_ble_result_failure_phone_body">Dein Telefon hat überhaupt keine BLE-Daten empfangen. Du könntest diesen Test in einem überfüllten Bereich wiederholen, um zu sehen, ob Datenquellen (außer Ihren Kopfhörern) empfangen werden können. Es werden keine Daten empfangen, was auf ein Problem mit dem Betriebssystem Ihres Telefons hindeutet.</string>
<string name="troubleshooter_ble_result_failure_phone_headphones">Ihr Telefon hat BLE-Daten empfangen, aber die Daten stammen von keinem unterstützten Gerät. Sind Ihre Kopfhörer eingeschaltet? Unterstützt CAPod Ihren Kopfhörer?</string>
<string name="troubleshooter_ble_result_failure_action">Versuchen Sie es erneut</string>
<string name="troubleshoot_action">Fehlerbehebung</string>
+29 -3
View File
@@ -32,15 +32,23 @@
<string name="settings_reaction_label">Αντιδράσεις</string>
<string name="settings_reaction_description">Αντίδραση σε συμβάντα και συμπεριφορές.</string>
<string name="settings_category_yourdevice_label">Η συσκευή σας</string>
<string name="settings_category_compatibility_options_title">Ρυθμίσεις συμβατότητας</string>
<string name="settings_category_compatibility_options_description">Μην αγγίζεις άμα όλα δουλεύουν ;)</string>
<string name="settings_compat_offloaded_filtering_disabled_title">Απενεργοποίηση φιλτραρίσματος υλικού</string>
<string name="settings_compat_offloaded_filtering_disabled_summary">Μην μεταβιβάζετε φιλτράρισμα δεδομένων στο σύστημα, αλλά λάβετε όλα τα δεδομένα και φιλτράρετε τα μέσα στην εφαρμογή.</string>
<string name="settings_compat_offloaded_batching_disabled_title">Απενεργοποίηση παρτίδας υλικού</string>
<string name="settings_compat_offloaded_batching_disabled_summary">Μην αφήσετε την ομάδα συστήματος να συλλέξει δεδομένα BLE πριν τα προχωρήσει σε εμάς.</string>
<string name="settings_maindevice_address_label">Διεύθυνση της συσκευής σας</string>
<string name="settings_maindevice_address_description">Η διεύθυνση της αντιστοιχισμένης συσκευής σας. Η εφαρμογή τη χρησιμοποιεί για να προσδιορίσει εάν η συσκευή σας είναι συνδεδεμένη στο τηλέφωνό σας.</string>
<string name="settings_maindevice_address_none">Καμία</string>
<string name="settings_maindevice_model_label">Μοντέλο της συσκευής σας</string>
<string name="settings_maindevice_model_description">Το μοντέλο της κύριας συσκευής σας. Αυτό βοηθά την εφαρμογή να αναγνωρίσει τη συσκευή σας όταν δεν είναι συνδεδεμένη στο τηλέφωνό σας.</string>
<string name="settings_popup_caseopen_label">Εμφάνιση αναδυόμενου</string>
<string name="settings_popup_caseopen_description">Εμφάνιση αναδυόμενου όταν η θήκη της συσκευής είναι ανοιχτή (πειραματικό).</string>
<string name="settings_onepod_mode_label">Λειτουργία ενός pod</string>
<string name="settings_onepod_mode_description">Δεν απαιτείται να φοράτε και τα δύο pods, αρκεί να φοράτε ένα μόνο pod για να ενεργοποιούνται αντιδράσεις.</string>
<string name="settings_popup_caseopen_label">Εμφάνιση πληροφοριών θήκης</string>
<string name="settings_popup_caseopen_description">Εμφάνιση αναδυόμενου όταν η θήκη της συσκευής είναι ανοιχτή (πειραματικό).</string>
<string name="settings_popup_connected_label">Εμφάνιση παραθυράκι σύνδεσης</string>
<string name="settings_popup_connected_description">Εμφάνιση παραθύρου όταν η συσκευή συνδέεται για πρώτη φορά.</string>
<string name="notification_channel_device_status_label">Κατάσταση συσκευής</string>
<string name="debug_debuglog_size_label">Μέγεθος</string>
<string name="debug_debuglog_size_compressed_label">Συμπιεσμένο μέγεθος</string>
@@ -53,6 +61,7 @@
<string name="settings_support_description">Εάν χρειάζεστε κάποια βοήθεια.</string>
<string name="issue_tracker_label">Εργαλείο παρακολούθησης θεμάτων</string>
<string name="issue_tracker_description">Δημόσιο εργαλείο παρακολούθησης θεμάτων για αναφορές σφαλμάτων και αιτήματα για δυνατότητες (μόνο αγγλικά).</string>
<string name="discord_label">Discord</string>
<string name="discord_description">Ένα μέρος για να αράξετε και να κάνετε ερωτήσεις.</string>
<string name="changelog_label">Αρχείο αλλαγών</string>
<string name="settings_label">Ρυθμίσεις</string>
@@ -74,5 +83,22 @@
<string name="help_translate_label">Μετάφραση</string>
<string name="help_translate_description">Βοηθήστε να μεταφραστεί αυτή η εφαρμογή στην αγαπημένη σας γλώσσα.</string>
<string name="translators_thanks_title">Μεταφραστές</string>
<string name="translators_thanks_description">Vagelis1608</string>
<string name="translators_thanks_description">Vagelis1608;Theodoros Asimakopoulos;gogoboss_stampolidis;Matthias;Christos Chronopoulos</string>
<string name="widget_description">Ένα γραφικό στοιχείο που δείχνει την τελευταία γνωστή κατάσταση συσκευής.</string>
<string name="settings_compat_indirectcallback_title">Έμμεση παράδοση δεδομένων</string>
<string name="settings_compat_indirectcallback_summary">Χρησιμοποιήστε εναλλακτική μέθοδο για να λάβετε δεδομένα BLE από το σύστημα (broadcast αντί για callback).</string>
<string name="troubleshooter_title">Αντιμετώπιση προβλημάτων</string>
<string name="troubleshooter_ble_intro_title">Εκπομπές Χαμηλής Ενέργειας Bluetooth</string>
<string name="troubleshooter_ble_intro_body1">Τα AirPods (και παρόμοια ακουστικά) μεταδίδουν πληροφορίες κατάστασης χρησιμοποιώντας μια τεχνολογία BLE που ονομάζεται \"διαφημίσεις\". Ορισμένα τηλέφωνα δεν εφαρμόζουν σωστά αυτήν την τεχνολογία. Το CAPod μπορεί να προσπαθήσει να το διορθώσει δοκιμάζοντας διαφορετικές επιλογές συμβατότητας μέχρι να ληφθούν δεδομένα. Ξεκινήστε την αναπαραγωγή μουσικής στα ακουστικά σας και τοποθετήστε τα κοντά στο τηλέφωνό σας και, στη συνέχεια, ξεκινήστε τη διαδικασία.</string>
<string name="troubleshooter_ble_intro_start_action">Ξεκινήστε την αντιμετώπιση προβλημάτων</string>
<string name="troubleshooter_ble_process_title">Αντιμετώπιση προβλημάτων σε εξέλιξη</string>
<string name="troubleshooter_ble_process_subtile">Κάθε βήμα διαρκεί 5-10 δευτερόλεπτα</string>
<string name="troubleshooter_ble_result_success_title">Επιτυχία</string>
<string name="troubleshooter_ble_result_success_body">Οι διαφημιστικές εκπομπές BLE λαμβάνονται από το CAPod.</string>
<string name="troubleshooter_ble_result_failure_title">Ανεπιτυχής</string>
<string name="troubleshooter_ble_result_failure_body">Η αντιμετώπιση προβλημάτων απέτυχε. Κανένας συνδυασμός επιλογών συμβατότητας δεν βοήθησε.</string>
<string name="troubleshooter_ble_result_failure_phone_body">Το τηλέφωνό σας δεν έλαβε καθόλου δεδομένα BLE. Μπορείτε να δοκιμάσετε ξανά αυτήν τη δοκιμή σε μια πολυσύχναστη περιοχή για να δείτε εάν μπορούν να ληφθούν πηγές δεδομένων (εκτός από τα ακουστικά σας). Δε λαμβάνονται δεδομένα που δείχνουν ένα πρόβλημα με το λειτουργικό σύστημα του τηλεφώνου σας.</string>
<string name="troubleshooter_ble_result_failure_phone_headphones">Το τηλέφωνό σας έλαβε δεδομένα BLE, αλλά τα δεδομένα δεν προέρχονται από καμία υποστηριζόμενη συσκευή. Είναι ενεργοποιημένα τα ακουστικά σας; Το CAPod υποστηρίζει τα ακουστικά σας;</string>
<string name="troubleshooter_ble_result_failure_action">Δοκιμάστε ξανά</string>
<string name="troubleshoot_action">Αντιμετώπιση</string>
</resources>
+55 -29
View File
@@ -7,72 +7,98 @@
<string name="general_upgrade_action">Actualizar</string>
<string name="general_check_action">Verificar</string>
<string name="general_close_action">Cerrar</string>
<string name="upgrade_capod_label">Actualitzar CAPod</string>
<string name="upgrade_capod_description">Obtenga funciones adicionales y apoye al desarrollador.</string>
<string name="upgrade_capod_label">Actualizar CAPod</string>
<string name="upgrade_capod_description">Consigue características adicionales y apoya al desarrollador.</string>
<string name="settings_monitor_mode_label">Modo monitor</string>
<string name="settings_monitor_mode_description">Bajo qué circunstancias esta aplicación monitorea los datos Bluetooth.</string>
<string name="settings_scanner_mode_label">Modo escáner</string>
<string name="settings_scanner_mode_description">¿El escáner de datos Bluetooth Low Energy debe priorizar el rendimiento o ahorrar energía?</string>
<string name="settings_autopause_label">Pausar automáticamente</string>
<string name="settings_autopause_description">Pausa el audio cuando te quites el dispositivo de la oreja.</string>
<string name="settings_scanner_mode_description">¿El escáner de datos del Bluetooth de baja energía debe priorizar el rendimiento o conservar la batería?</string>
<string name="settings_autopause_label">Pausa automática</string>
<string name="settings_autopause_description">Pausa el audio cuando retires el dispositivo del oído.</string>
<string name="settings_showall_label">Mostrar todos los dispositivos</string>
<string name="settings_showall_description">Muestra los dispositivos de otras personas que están cerca de ti.</string>
<string name="settings_showall_description">Muestra los dispositivos de otras personas que están cerca.</string>
<string name="settings_autopplay_label">Reproducción automática</string>
<string name="settings_autoplay_description">Inicia la reproducción de audio cuando se use el dispositivo.</string>
<string name="settings_autoplay_description">Inicia la reproducción del audio cuando se usa el dispositivo.</string>
<string name="settings_fake_data_label">Información falsa</string>
<string name="settings_fake_data_description">Mostrar datos falsos. Por ejemplo: simular un dispositivo que no existe.</string>
<string name="settings_debug_label">Configuración de depuración</string>
<string name="settings_debug_description">Configuraciones adicionales para ayudar a solucionar problemas con la aplicación.</string>
<string name="settings_signal_minimum_label">Calidad de señal mínima</string>
<string name="settings_signal_minimum_description">La calidad de señal mínima que debe tener un dispositivo para ser considerado suyo.</string>
<string name="settings_autoconnect_label">Autoconectar</string>
<string name="settings_fake_data_description">Mostrar los datos falsos, es decir, simular dispositivos que no existen.</string>
<string name="settings_debug_label">Opciones de depuración</string>
<string name="settings_debug_description">Ajustes adicionales para ayudar a solucionar los problemas con la aplicación.</string>
<string name="settings_signal_minimum_label">Calidad mínima de la señal</string>
<string name="settings_signal_minimum_description">La calidad mínima de la señal que debe tener un dispositivo para ser considerado tuyo.</string>
<string name="settings_autoconnect_label">Conectar automáticamente</string>
<string name="settings_autoconnect_description">Si Android no se conecta automáticamente, también podemos preguntarlo. Esto establecerá la configuración del modo de monitor en \'Siempre\'.</string>
<string name="settings_autoconnect_condition_label">Condición de autoconexión</string>
<string name="settings_autoconnect_condition_description">¿Cuándo debemos intentar conectarnos a su dispositivo?</string>
<string name="settings_reaction_label">Reacciones</string>
<string name="settings_reaction_description">Reaccionar a eventos y comportamientos.</string>
<string name="settings_category_yourdevice_label">Tu dispositivo</string>
<string name="settings_category_compatibility_options_title">Compatibilidad</string>
<string name="settings_category_compatibility_options_description">No toques nada si todo funciona ;)</string>
<string name="settings_compat_offloaded_filtering_disabled_title">Desactivar el filtrado por hardware</string>
<string name="settings_compat_offloaded_filtering_disabled_summary">No delegues el filtrado de los datos al sistema, en cambio obtén todos los datos y fíltralos dentro de la aplicación.</string>
<string name="settings_compat_offloaded_batching_disabled_title">Deshabilitar el procesamiento por lotes del hardware</string>
<string name="settings_compat_offloaded_batching_disabled_summary">No permitas que el sistema recopile los datos BLE antes de enviárnoslos.</string>
<string name="settings_maindevice_address_label">La dirección de su dispositivo</string>
<string name="settings_maindevice_address_description">La dirección de su dispositivo emparejado. La aplicación usa esto para determinar cuándo está conectado a su teléfono.</string>
<string name="settings_maindevice_address_none">Ninguno</string>
<string name="settings_maindevice_model_label">El modelo de su dispositivo</string>
<string name="settings_maindevice_model_description">El modelo de su dispositivo principal. Esto ayuda a que la aplicación reconozca su dispositivo cuando no está conectado a su teléfono.</string>
<string name="settings_popup_caseopen_label">Mostrar ventanas emergentes</string>
<string name="settings_popup_caseopen_description">Muestra una ventana emergente cuando se abre el estuche del dispositivo (experimental).</string>
<string name="settings_onepod_mode_label">Modo un audífono</string>
<string name="settings_onepod_mode_description">No es necesario usar ambos AirPods, usar un solo AirPod es suficiente para desencadenar reacciones.</string>
<string name="settings_popup_caseopen_label">Mostrar una ventana emergente</string>
<string name="settings_popup_caseopen_description">Muestra una ventana emergente cuando se abre el estuche del dispositivo (experimental).</string>
<string name="settings_popup_connected_label">Mostrar una ventana emergente con la conexión</string>
<string name="settings_popup_connected_description">Mostrar una ventana emergente cuando el dispositivo se conecta por primera vez.</string>
<string name="notification_channel_device_status_label">Estado del dispositivo</string>
<string name="debug_debuglog_size_label">Tamaño</string>
<string name="debug_debuglog_size_compressed_label">Tamaño comprimido</string>
<string name="debug_notification_channel_label">Notificaciones depuradas</string>
<string name="debug_notification_channel_label">Notificaciones de la depuración</string>
<string name="debug_debuglog_file_label">Registro guardado</string>
<string name="debug_debuglog_record_action">Grabar registro de depuracion</string>
<string name="settings_support_installid_label">ID de instalación</string>
<string name="settings_support_installid_desc">Los informes automáticos de errores son anónimos. Comparta su ID de instalación si el desarrollador necesita encontrar sus informes de error.</string>
<string name="debug_debuglog_record_action">Registro de depuración</string>
<string name="settings_support_installid_label">ID de la instalación</string>
<string name="settings_support_installid_desc">Los informes automáticos de errores son anónimos. Comparte tu ID de la instalación si el desarrollador necesita encontrar tus informes de errores.</string>
<string name="settings_support_label">Ayuda</string>
<string name="settings_support_description">Si necesita ayuda.</string>
<string name="issue_tracker_label">Rastreador de problemas</string>
<string name="issue_tracker_description">Un rastreador público de problemas para informes de errores y solicitudes de funciones (solo en inglés).</string>
<string name="discord_description">Un lugar para pasar el rato y hacer preguntas.</string>
<string name="settings_support_description">Si necesitas ayuda.</string>
<string name="issue_tracker_label">Gestor de incidencias</string>
<string name="issue_tracker_description">Un gestor de incidencias público para los informes de errores y solicitudes de funciones (solo en inglés).</string>
<string name="discord_label">Discord</string>
<string name="discord_description">Un lugar donde pasar el rato y hacer preguntas.</string>
<string name="changelog_label">Registro de cambios</string>
<string name="settings_label">Ajustes</string>
<string name="settings_privacy_policy_label">Política de privacidad</string>
<string name="settings_privacy_policy_desc">Manejo responsable de datos.</string>
<string name="settings_privacy_policy_desc">Tratamiento responsable de los datos.</string>
<string name="settings_licenses_label">Licencias</string>
<string name="settings_category_other_label">Otros</string>
<string name="settings_general_label">Ajustes</string>
<string name="settings_general_description">Ajustes generales que afectan a toda la aplicación.</string>
<string name="settings_acknowledgements_label">Agradecimientos</string>
<string name="settings_debug_autoreports_label">Informes automáticos de errores</string>
<string name="settings_debug_autoreports_description">Informa automáticamente de los problemas. Por ejemplo: detalles sobre un bloqueo de la aplicación para que pueda averiguar cómo solucionarlo.</string>
<string name="settings_debug_mode_label">Modo depuración</string>
<string name="settings_debug_mode_description">Muestra información adicional para solucionar problemas.</string>
<string name="settings_debug_autoreports_description">Informa automáticamente de los problemas, por ejemplo, de los detalles de un fallo de la aplicación para que pueda averiguar cómo solucionarlo.</string>
<string name="settings_debug_mode_label">Modo de depuración</string>
<string name="settings_debug_mode_description">Mostrar información adicional para solucionar los problemas.</string>
<string name="settings_blescanner_unfiltered_label">Datos BLE sin filtrar</string>
<string name="settings_blescanner_unfiltered_description">Elimina los filtros del escáner BLE para mostrar todos los datos BLE transmitidos. Útil para agregar soporte para nuevos tipos de auriculares.</string>
<string name="settings_blescanner_unfiltered_description">Elimina cualquier filtro del escáner BLE para mostrar todos los datos BLE emitidos. Útil para añadir compatibilidad con nuevos tipos de auriculares.</string>
<string name="settings_compatibility_mode_label">Modo de compatibilidad</string>
<string name="settings_compatibility_mode_description">Deshabilita las optimizaciones para mejorar la compatibilidad. Prueba esto si no ves ningún dato.</string>
<string name="settings_compatibility_mode_description">Desactiva las optimizaciones para mejorar la compatibilidad. Prueba esto si no ves ningún dato.</string>
<string name="help_translate_label">Traducción</string>
<string name="help_translate_description">Ayuda a traducir esta aplicación a tu idioma favorito.</string>
<string name="translators_thanks_title">Traductores</string>
<string name="translators_thanks_description">Jaime Muñoz(jmmartin_5@outlook.com)</string>
<string name="widget_description">Un widget que muestra como estaba tu dispositivo.</string>
<string name="settings_compat_indirectcallback_title">Envío indirecto de datos</string>
<string name="settings_compat_indirectcallback_summary">Utiliza un método alternativo para recibir los datos BLE del sistema (emisión en lugar de devolución de llamada).</string>
<string name="troubleshooter_title">Reparador de problemas</string>
<string name="troubleshooter_ble_intro_title">Transmisiones Bluetooth de bajo consumo</string>
<string name="troubleshooter_ble_intro_body1">Los AirPods (y auriculares similares) transmiten información de su estado mediante una tecnología BLE llamada \"anuncios\". Algunos teléfonos no implementan esta tecnología correctamente. CAPod puede intentar arreglar esto probando diferentes opciones de compatibilidad hasta que se reciban los datos. Inicia la reproducción de la música en tus auriculares y colócalos cerca de tu teléfono, luego comienza el proceso.</string>
<string name="troubleshooter_ble_intro_start_action">Empezar a resolver los problemas</string>
<string name="troubleshooter_ble_process_title">Solución de los problemas en curso</string>
<string name="troubleshooter_ble_process_subtile">Cada paso dura entre 5 y 10 segundos</string>
<string name="troubleshooter_ble_result_success_title">Correcto</string>
<string name="troubleshooter_ble_result_success_body">CAPod recibe las emisiones de anuncios BLE.</string>
<string name="troubleshooter_ble_result_failure_title">Incorrecto</string>
<string name="troubleshooter_ble_result_failure_body">La solución de problemas falló. Ninguna combinación de las opciones de compatibilidad ayudó.</string>
<string name="troubleshooter_ble_result_failure_phone_body">Tu teléfono no ha recibido ningún dato BLE. Puedes volver a intentar esta prueba en una zona concurrida para ver si se pueden recibir fuentes de datos (distintas de los auriculares). El hecho de que no se reciban datos apunta a un problema con el sistema operativo del teléfono.</string>
<string name="troubleshooter_ble_result_failure_phone_headphones">Tu teléfono recibió los datos BLE, pero los datos no proceden de ningún dispositivo compatible. ¿Están encendidos tus auriculares? ¿Es CAPod compatible con tus auriculares?</string>
<string name="troubleshooter_ble_result_failure_action">Vuelve a intentarlo</string>
<string name="troubleshoot_action">Reparador de los problemas</string>
</resources>
@@ -4,7 +4,11 @@
<string name="settings_category_compatibility_options_description">Las jääda, kui kõik toimib ;)</string>
<string name="settings_compat_offloaded_filtering_disabled_title">Keela riistvara filtreerimine</string>
<string name="settings_compat_offloaded_filtering_disabled_summary">Süsteemis andmete filtreerimise asemel saate seda teha rakenduses.</string>
<string name="settings_compat_offloaded_batching_disabled_title">Keela riistvara rühmitamine</string>
<string name="settings_compat_offloaded_batching_disabled_summary">Keelake süsteemil rühmitada kogutud andmeid madalama energiatarbimisega Bluetoothi kohta enne meile saatmist.</string>
<string name="settings_popup_caseopen_label">Näita kaane hüpikakent</string>
<string name="settings_popup_connected_label">Näita ühenduse hüpikakent</string>
<string name="settings_popup_connected_description">Näita seadme esmaühendumisel hüpikakent.</string>
<string name="widget_description">Viimast teadaolevat seadme seisu näitab vidin.</string>
<string name="settings_compat_indirectcallback_title">Kaudsete andmete edastamine</string>
<string name="settings_compat_indirectcallback_summary">Süsteemis vähem energiat tarbiva Bluetoothi andmete saamine muul viisil (edasta tagasi kutsumise asemel).</string>

Some files were not shown because too many files have changed in this diff Show More