diff --git a/.gitea/ISSUE_TEMPLATE.md b/.gitea/ISSUE_TEMPLATE.md deleted file mode 100644 index f6dedd485..000000000 --- a/.gitea/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- -You can use the `Preview` tab ^ above to see final rendering of your report. Use `x` in brackets ([x]) to "check" a checkbox. - -If you just have a question, please ask first in the user chatroom in Matrix: [`#gadgetbridge:matrix.org`](https://matrix.to/#/#gadgetbridge:matrix.org) - - -#### Before reporting a bug, please confirm the following: -- [ ] I have read the [wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki), and I didn't find a solution to my problem / an answer to my question. -- [ ] I have searched the [issues](https://codeberg.org/Freeyourgadget/Gadgetbridge/issues), and I didn't find a solution to my problem / an answer to my question. -- [ ] If you upload an image or other content, please make sure you have read and understood the [Codeberg Terms of Use](https://codeberg.org/Codeberg/org/src/branch/main/TermsOfUse.md) - -### Where did you get Gadgetbridge from: -* [ ] F-Droid -* [ ] Gadgetbridge Nightly F-Droid repository -* [ ] Bangle.js Gadgetbridge from the Play Store -* [ ] I built it myself from source code (specify tag / commit) -* [ ] I previously used Gadgetbridge from other sources and then updated to F-Droid version - -### Your Gadgetbridge version is: - -(This can be found in Gadgetbridge - Menu - About: Version and Commit) - -#### Your issue is: -*If possible, please attach [logs](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Log-Files)! that might help identifying the problem.* -*Long logs can be also included but make sure to tuck them into the `details` tag:* - -
- Click to see my log under this fold - -``` -Here go lines of your log. -``` -
- - -#### Your wearable device is: - -*Please specify model and firmware version if possible* - -#### Your Android version/manufacturer flavor is: - - -*New issues about already solved/documented topics could be closed without further comments. Same for too generic or incomplete reports.* - -Please use `Preview` tab above to see final rendering of your report before submitting. diff --git a/.gitea/ISSUE_TEMPLATE/bug_report.md b/.gitea/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index f6dedd485..000000000 --- a/.gitea/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- -You can use the `Preview` tab ^ above to see final rendering of your report. Use `x` in brackets ([x]) to "check" a checkbox. - -If you just have a question, please ask first in the user chatroom in Matrix: [`#gadgetbridge:matrix.org`](https://matrix.to/#/#gadgetbridge:matrix.org) - - -#### Before reporting a bug, please confirm the following: -- [ ] I have read the [wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki), and I didn't find a solution to my problem / an answer to my question. -- [ ] I have searched the [issues](https://codeberg.org/Freeyourgadget/Gadgetbridge/issues), and I didn't find a solution to my problem / an answer to my question. -- [ ] If you upload an image or other content, please make sure you have read and understood the [Codeberg Terms of Use](https://codeberg.org/Codeberg/org/src/branch/main/TermsOfUse.md) - -### Where did you get Gadgetbridge from: -* [ ] F-Droid -* [ ] Gadgetbridge Nightly F-Droid repository -* [ ] Bangle.js Gadgetbridge from the Play Store -* [ ] I built it myself from source code (specify tag / commit) -* [ ] I previously used Gadgetbridge from other sources and then updated to F-Droid version - -### Your Gadgetbridge version is: - -(This can be found in Gadgetbridge - Menu - About: Version and Commit) - -#### Your issue is: -*If possible, please attach [logs](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Log-Files)! that might help identifying the problem.* -*Long logs can be also included but make sure to tuck them into the `details` tag:* - -
- Click to see my log under this fold - -``` -Here go lines of your log. -``` -
- - -#### Your wearable device is: - -*Please specify model and firmware version if possible* - -#### Your Android version/manufacturer flavor is: - - -*New issues about already solved/documented topics could be closed without further comments. Same for too generic or incomplete reports.* - -Please use `Preview` tab above to see final rendering of your report before submitting. diff --git a/.gitea/ISSUE_TEMPLATE/device_request.md b/.gitea/ISSUE_TEMPLATE/device_request.md deleted file mode 100644 index 1470500df..000000000 --- a/.gitea/ISSUE_TEMPLATE/device_request.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -name: Device request -about: Request for a new device -labels: -- device request ---- - -You are trying to submit a request for a device implementation into Gadgetbridge. We cannot make any promise that the device will be implemented, as adding an implementation for a new device requires either an experienced or a willing to learn developer, ideally with the device at hand. Without that, you may try to submit a device request and see if anyone steps up and implements it and you are basically indicating that you have the device and are willing to help with testing, should anyone try to add this device to Gadgetbridge. - -You can use the `Preview` tab ^ above to see final rendering of your report. Use `x` in brackets ([x]) to "check" a checkbox. - -#### Before proceeding further, please confirm the following: -- [ ] I have read the [wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki), and I didn't find this device mentioned there -- [ ] I have searched the [issues](https://codeberg.org/Freeyourgadget/Gadgetbridge/issues), and I didn't find this device mentioned there -- [ ] Please make sure you have read and understood the [Codeberg Terms of Use](https://codeberg.org/Codeberg/org/src/branch/main/TermsOfUse.md) - -#### Device information - -- Provide device name, manufacturer and similarity to other devices: - - -- Ideally, use an Android Bluetooth scanner app like nRF Connect or BLExplorer and provide screenshots of the scanned device from that app. This provides a name and some available UUIDs, which are needed for implementation. You may want to blur a MAC address for privacy reasons. - - -- Specify model and firmware version if possible: - - -- Please let us know if you have the device and if you are able to provide logs and testing: - - -- If you want to help getting this device supported (and thus speed up the process), please take a look at: - - what you can do as a user: [Support for a new Device](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Support-for-a-new-Device) - - if you are a developer and could maybe even help getting the code ready: [New Device Tutorial](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/New-Device-Tutorial) - -- If possible, please attach [logs](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Log-Files)! which might help to implement the device support into Gadgetbridge. -*Long logs can be also included in the text but make sure to tuck them into the `details` tag below:* - -
- Click to see my log under this fold - -``` -Here go lines of your log. -``` -
- - - - -*New issues about already solved/documented topics could be closed without further comments. Same for too generic or incomplete reports.* - -Please use `Preview` tab above to see final rendering of your report before submitting. diff --git a/.gitea/ISSUE_TEMPLATE/feature_request.md b/.gitea/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 4e92a04aa..000000000 --- a/.gitea/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: Feature request -about: Suggest a feature or an idea ---- -You can use the `Preview` tab ^ above to see final rendering of your report. Use `x` in brackets ([x]) to "check" a checkbox. - -If you just have a question, please ask first in the user chatroom in Matrix: [`#gadgetbridge:matrix.org`](https://matrix.to/#/#gadgetbridge:matrix.org) - -#### Before requesting a new feature, please confirm the following: -- [ ] I have read the [wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki), and I didn't find a solution to my problem / an answer to my question. -- [ ] I have searched the [issues](https://codeberg.org/Freeyourgadget/Gadgetbridge/issues), and I didn't find a solution to my problem / an answer to my question. -- [ ] If you upload an image or other content, please make sure you have read and understood the [Codeberg Terms of Use](https://codeberg.org/Codeberg/org/src/branch/main/TermsOfUse.md) - -#### Log files -*If applicable, please attach [logs](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Log-Files)* -*Long logs can be also included in the text but make sure to tuck them into the `details` tag below:* - -
- Click to see my log under this fold - -``` -Here go lines of your log. -``` -
- - -#### Your wearable device is: - -*Please specify model and firmware version if possible* - -#### Your Android version/manufacturer flavor is: - -#### Your Gadgetbridge version is: - - - -*New requests about already solved/documented topics could be closed without further comments. Same for too generic or incomplete reports.* - -Please use `Preview` tab above to see final rendering of your report before submitting. - diff --git a/.gitea/PULL_REQUEST_TEMPLATE.md b/.gitea/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 5b92d9460..000000000 --- a/.gitea/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,14 +0,0 @@ -Thank you for your contribution! 🎉 - -Please make sure that you: - -- Use `git rebase` to bring your branch up to date, do not use a merge commit - for that -- Do not add translations by editing the language variants of strings.xml as - that creates merge conflicts between Codeberg git repo and Weblate git repo, - use Weblate https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge/ -- do not use `e.printStacktrace()`, use slf4j logger or `GB.toast` for logging - as per https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Developer-Documentation#logging -- Please erase these hints from this PR :), thank you! ---- - diff --git a/.gitea/issue_template/bug_report.yml b/.gitea/issue_template/bug_report.yml new file mode 100644 index 000000000..a4c186ae0 --- /dev/null +++ b/.gitea/issue_template/bug_report.yml @@ -0,0 +1,77 @@ +name: Bug report +about: Create a report to help us improve. +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to make Gadgetbridge better! + + If you just have a question, please ask first in the user chatroom in Matrix at [`#gadgetbridge:matrix.org`](https://matrix.to/#/#gadgetbridge:matrix.org) + - type: checkboxes + attributes: + label: Please confirm that; + options: + - label: I have checked the [website](https://gadgetbridge.org), and I didn't find a solution to my problem / an answer to my question. + required: true + - label: I have searched the [issues](https://codeberg.org/Freeyourgadget/Gadgetbridge/issues), and I didn't find a solution to my problem / an answer to my question. + required: true + - label: I have read and understood the [Codeberg Terms of Use](https://codeberg.org/Codeberg/org/src/branch/main/TermsOfUse.md) for images or other type of content that I included here. + required: true + - type: dropdown + id: source + attributes: + label: Where did you get Gadgetbridge from? + options: + - F-Droid + - Gadgetbridge Nightly F-Droid repository + - Bangle.js Gadgetbridge from the Play Store + - I built it myself from source code + - I previously used Gadgetbridge from other sources and then updated to F-Droid version + validations: + required: true + - type: input + id: version + attributes: + label: What is your Gadgetbridge version? + description: | + This can be found in "Menu > About > Version" in Gadgetbridge. + Also include tag / commit SHA if you built Gadgetbridge from the source. + placeholder: e.g. "0.77.0" or "0.77.0-2618adac1" to include commit + validations: + required: true + - type: textarea + id: content + attributes: + label: What happened? + description: > + Please note that new issues about already solved/documented topics + **could be closed without further comments.** Same for too generic or incomplete reports. + placeholder: If you want to include logs, don't include it here. Use the next text field for that. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Do you have logs? + description: > + If possible, please attach [logs](https://gadgetbridge.org/internals/topics/logs/) + that might help identifying the problem. This will be automatically formatted into + code, so no need for backticks. + render: shell + - type: input + id: gadget + attributes: + label: What gadget do you use? + description: > + Please specify model and firmware version if possible. Leave blank if you believe the + issue is not specific to the gadget that you currently use with Gadgetbridge. + placeholder: e.g. ExampleWatch A1 with 0.1 firmware + - type: input + id: android + attributes: + label: What is your Android version/manufacturer flavor? + description: > + Android phone manufacturers may customise the Android source code as they wish, so + if you are using a phone that running a vendor-exclusive system (like MIUI) or if + you use a custom ROM, make sure to also include the name of the OS/ROM. + placeholder: e.g. LineageOS 20 based on Android 13 \ No newline at end of file diff --git a/.gitea/issue_template/config.yml b/.gitea/issue_template/config.yml new file mode 100644 index 000000000..4abd84b80 --- /dev/null +++ b/.gitea/issue_template/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://matrix.to/#/#gadgetbridge:matrix.org + about: If you just have a question, please ask in the user chatroom in Matrix. + - name: Website + url: https://gadgetbridge.org + about: Visit Gadgetbridge website for FAQ, getting started and common troubleshooting. + - name: See supported gadgets + url: https://gadgetbridge.org/gadgets/ + about: List of all supported gadget vendors and models in Gadgetbridge. \ No newline at end of file diff --git a/.gitea/issue_template/device_request.yml b/.gitea/issue_template/device_request.yml new file mode 100644 index 000000000..724f12054 --- /dev/null +++ b/.gitea/issue_template/device_request.yml @@ -0,0 +1,77 @@ +name: Device request +about: Request for a new device/gadget. +labels: + - device request +body: + - type: markdown + attributes: + value: > + You are trying to submit a request for a device implementation into Gadgetbridge. + **We cannot make any promise that the device will be implemented**, as adding an implementation + for a new device requires either an experienced or a willing to learn developer, + ideally with the device at hand. Without that, you may try to submit a device request + and see if anyone steps up and implements it and you are basically indicating that you + have the device and are willing to help with testing, should anyone try to add + this device to Gadgetbridge. + - type: markdown + attributes: + value: | + Thanks for taking the time to make Gadgetbridge better! + + If you just have a question, please ask first in the user chatroom in Matrix at [`#gadgetbridge:matrix.org`](https://matrix.to/#/#gadgetbridge:matrix.org) + - type: checkboxes + attributes: + label: Please confirm that; + options: + - label: I have checked the [website](https://gadgetbridge.org) and [gadget list](https://gadgetbridge.org/gadgets/), and I didn't find this device mentioned there. + required: true + - label: I have searched the [issues](https://codeberg.org/Freeyourgadget/Gadgetbridge/issues), and I didn't find this device mentioned there. + required: true + - label: I have read and understood the [Codeberg Terms of Use](https://codeberg.org/Codeberg/org/src/branch/main/TermsOfUse.md) for images or other type of content that I included here. + required: true + - type: input + id: gadget + attributes: + label: Which gadget are you requesting support for? + description: Please specify name, manufacturer, model and firmware version if possible. + placeholder: e.g. ExampleWatch A1 with 0.1 firmware + validations: + required: true + - type: input + id: version + attributes: + label: Does this gadget have similarities to other gadgets? + description: > + If the gadget you request support for has similarities to other gadgets that we currently support, + it is more likely for us (still, not guaranteed) to add support for your gadget too. + - type: textarea + id: info + attributes: + label: Device information + description: > + Include all details about the gadget that will be useful to us when implementing a support for this gadget. + + Ideally, use an Android Bluetooth scanner app like nRF Connect or BLExplorer and provide screenshots + of the scanned gadget from that app. This provides a name and some available UUIDs, which are needed + for implementation. You may want to blur MAC addresses for privacy reasons. + placeholder: If you want to include logs, don't include it here. Use the next text field for that. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Do you have logs? + description: > + If possible, please attach [logs](https://gadgetbridge.org/internals/topics/logs/) + which might help to implement the device support into Gadgetbridge. This will be automatically + formatted into code, so no need for backticks. + render: shell + - type: dropdown + id: source + attributes: + label: Do you have the gadget and are you able to help us for testing? + options: + - Yes, I have the gadget and I'm willing to provide further feedback and help testing. + - No, I don't have the gadget or/and I'm not able to help during the development. + validations: + required: true \ No newline at end of file diff --git a/.gitea/issue_template/feature_request.yml b/.gitea/issue_template/feature_request.yml new file mode 100644 index 000000000..cdabcef1a --- /dev/null +++ b/.gitea/issue_template/feature_request.yml @@ -0,0 +1,76 @@ +name: Feature request +about: Suggest a feature or an idea. +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to make Gadgetbridge better! + + If you just have a question, please ask first in the user chatroom in Matrix at [`#gadgetbridge:matrix.org`](https://matrix.to/#/#gadgetbridge:matrix.org) + - type: checkboxes + attributes: + label: Please confirm that; + options: + - label: I have checked the [website](https://gadgetbridge.org), and I didn't find a solution to my problem / an answer to my question. + required: true + - label: I have searched the [issues](https://codeberg.org/Freeyourgadget/Gadgetbridge/issues), and I didn't find a solution to my problem / an answer to my question. + required: true + - label: I have read and understood the [Codeberg Terms of Use](https://codeberg.org/Codeberg/org/src/branch/main/TermsOfUse.md) for images or other type of content that I included here. + required: true + - type: dropdown + id: source + attributes: + label: Where did you get Gadgetbridge from? + options: + - F-Droid + - Gadgetbridge Nightly F-Droid repository + - Bangle.js Gadgetbridge from the Play Store + - I built it myself from source code + - I previously used Gadgetbridge from other sources and then updated to F-Droid version + validations: + required: true + - type: input + id: version + attributes: + label: What is your Gadgetbridge version? + description: | + This can be found in "Menu > About > Version" in Gadgetbridge. + Also include tag / commit SHA if you built Gadgetbridge from the source. + placeholder: e.g. "0.77.0" or "0.77.0-2618adac1" to include commit + validations: + required: true + - type: textarea + id: content + attributes: + label: What is your suggestion/idea? + description: > + Please note that new requests about already solved/documented topics + **could be closed without further comments.** Same for too generic or incomplete reports. + placeholder: If you want to include logs, don't include it here. Use the next text field for that. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Do you have logs? + description: > + If possible, please attach [logs.](https://gadgetbridge.org/internals/topics/logs/) + This will be automatically formatted into code, so no need for backticks. + render: shell + - type: input + id: gadget + attributes: + label: What gadget do you use? + description: > + Please specify model and firmware version if possible. Leave blank if you believe the + issue is not specific to the gadget that you currently use with Gadgetbridge. + placeholder: e.g. ExampleWatch A1 with 0.1 firmware + - type: input + id: android + attributes: + label: What is your Android version/manufacturer flavor? + description: > + Android phone manufacturers may customise the Android source code as they wish, so + if you are using a phone that running a vendor-exclusive system (like MIUI) or if + you use a custom ROM, make sure to also include the name of the OS/ROM. + placeholder: e.g. LineageOS 20 based on Android 13 \ No newline at end of file diff --git a/.gitea/pull_request_template.md b/.gitea/pull_request_template.md new file mode 100644 index 000000000..021ab4b7b --- /dev/null +++ b/.gitea/pull_request_template.md @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 19b292ea4..cc9db9855 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "fossil-hr-watchface"] - path = external/fossil-hr-watchface - url = https://codeberg.org/Freeyourgadget/fossil-hr-watchface [submodule "jerryscript"] path = external/jerryscript url = https://github.com/jerryscript-project/jerryscript +[submodule "fossil-hr-gbapps"] + path = external/fossil-hr-gbapps + url = https://codeberg.org/Freeyourgadget/fossil-hr-gbapps diff --git a/.woodpecker/can_master_build.yml b/.woodpecker/can_master_build.yml index dde54fb5c..fb239d34c 100644 --- a/.woodpecker/can_master_build.yml +++ b/.woodpecker/can_master_build.yml @@ -12,5 +12,8 @@ steps: #https://github.com/woodpecker-ci/woodpecker/issues/687 when: - event: - exclude: ['cron', 'deployment'] + # Everything except cron (nightly) and deployment + - event: push + - event: pull_request + - event: tag + - event: manual diff --git a/.woodpecker/nightly.yml b/.woodpecker/nightly.yml index b245e61fc..38894cc10 100644 --- a/.woodpecker/nightly.yml +++ b/.woodpecker/nightly.yml @@ -26,8 +26,8 @@ steps: - if [ -f .do_not_build ]; then return 0; fi # build the apks - echo "$${SIGNING_KEY}" | base64 -d > app/keystore.p12 - - ./gradlew assembleMainNightly -Dnightly_store_file="keystore.p12" -Dnightly_store_password="$${KEYSTOREPASS}" -Dnightly_key_alias="gadgetbridge" -Dnightly_key_password="$${KEYPASS}" - - ./gradlew assembleMainNopebble -Dnightly_store_file="keystore.p12" -Dnightly_store_password="$${KEYSTOREPASS}" -Dnightly_key_alias="gadgetbridge" -Dnightly_key_password="$${KEYPASS}" + - ./gradlew assembleMainlineNightly -Dnightly_store_file="keystore.p12" -Dnightly_store_password="$${KEYSTOREPASS}" -Dnightly_key_alias="gadgetbridge" -Dnightly_key_password="$${KEYPASS}" + - ./gradlew assembleMainlineNopebble -Dnightly_store_file="keystore.p12" -Dnightly_store_password="$${KEYSTOREPASS}" -Dnightly_key_alias="gadgetbridge" -Dnightly_key_password="$${KEYPASS}" - ./gradlew assembleBanglejsNightly -Dnightly_store_file="keystore.p12" -Dnightly_store_password="$${KEYSTOREPASS}" -Dnightly_key_alias="gadgetbridge" -Dnightly_key_password="$${KEYPASS}" secrets: [ signing_key, keystorepass, keypass ] diff --git a/.woodpecker/run_lint.yml b/.woodpecker/run_lint.yml index f78bb032c..83a6501eb 100644 --- a/.woodpecker/run_lint.yml +++ b/.woodpecker/run_lint.yml @@ -12,5 +12,8 @@ steps: #https://github.com/woodpecker-ci/woodpecker/issues/687 when: - event: - exclude: ['cron', 'deployment'] + # Everything except cron (nightly) and deployment + - event: push + - event: pull_request + - event: tag + - event: manual diff --git a/.woodpecker/run_tests.yml b/.woodpecker/run_tests.yml index 2c5a16fdc..cf5717f5a 100644 --- a/.woodpecker/run_tests.yml +++ b/.woodpecker/run_tests.yml @@ -12,5 +12,8 @@ steps: #https://github.com/woodpecker-ci/woodpecker/issues/687 when: - event: - exclude: ['cron', 'deployment'] + # Everything except cron (nightly) and deployment + - event: push + - event: pull_request + - event: tag + - event: manual diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cea7cb0e..602be018f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,35 +1,136 @@ ### Changelog -#### Next release (WIP) +#### 0.79.0 +* Initial support for Honor Magic Watch 2 +* Initial support for Mijia MHO-C303 +* Initial support for Nothing CMF Watch Pro +* Initial support for Sony WI-SP600N +* Experimental support for Redmi Watch 2 +* Experimental support for Xiaomi Smart Band 8 Pro +* Experimental support for Xiaomi Watch S1 Pro +* Experimental support for Xiaomi Watch S1 +* Experimental support for Xiaomi Watch S3 +* Galaxy Buds2 Pro: Fix recognition of some versions +* Huawei Watch GT 2: Fix pairing +* Redmi Smart Band Pro: Fix password digits +* Pebble: Fix app configuration page +* Pebble 2: Fix pairing issue +* PineTime: Fix weather forecast on InfiniTime's new simple weather +* Xiaomi: Fix sleep sometimes extending past the wakeup time +* Xiaomi: Request battery level and charging state periodically +* Xiaomi: Fix sleep stage parsing for some devices +* Zepp OS: Improve device discovery +* Zepp OS: Fix weather not working on some devices +* Zepp OS: Prevent crash when installing large firmware updates +* Fix sport activity summary group order +* Fix reconnection to devices failing occasionally + +#### 0.78.0 +* Initial support for Honor Band 3,4,5,6 +* Initial support for Huawei Band 4, 4 Pro, 6, 7, 3e, 4e +* Initial support for Huawei Talk Band B6 +* Initial support for Huawei Watch GT, GT 2 +* Initial support for Mijia LYWSD03MMC +* Initial support for Nothing Ear (2) +* Initial support for Nothing Ear (Stick) +* Experimental support for Honor Band 7 +* Experimental support for Redmi Watch 2 Lite +* Experimental support for Redmi Smart Band Pro +* Casio GBX100: Add support for snooze alarm +* Fossil/Skagen Hybrids: Update navigationApp to 1.1 +* Huami: Fetch SpO2 on devices that support it +* Pebble: Attempt to fix app configuration webview +* PineTime: Add support for InfiniTime's new simple weather +* PineTime: Fix freeze and reboot when upgrading firmware +* Pixoo: Enable sending images (non-persistent) +* Pixoo: Get and send alarms +* Pixoo: Set custom device name +* Pixoo: support "clap hands to turn off screen" and "sleep after silence" settings +* Xiaomi: Improve activity and workout parsing +* Xiaomi: Improve stability and fix some crashes +* Xiaomi: Improve weather +* Xiaomi: Parse sleep stages +* Add a notifications channel for connection status notifications +* Improve automatic connection to all or previous devices +* Fix devices sometimes staying stuck in a "Connecting" state +* Map some missing Google Maps navigation actions + +#### 0.77.0 * Initial support for Amazfit Balance +* Initial support for Amazfit Active +* Initial support for ColaCao 2021 +* Initial support for ColaCao 2023 * Initial support for Femometer Vinca II * Initial support for Mijia LYWSD02MMC variant * Initial support for Sony Wena 3 +* Experimental support for Divoom Pixoo * Experimental support for Sony WF-1000XM5 +* Experimental support for Amazfit Active Edge +* Experimental support for Mi Band 7 Pro (Xiaomi Smart Band 7 Pro) +* Experimental support for Mi Band 8 (Xiaomi Smart Band 8) +* Experimental support for Mi Watch Lite +* Experimental support for Mi Watch Color Sport +* Experimental support for Redmi Smart Band 2 +* Experimental support for Redmi Watch 3 Active +* Experimental support for Xiaomi Watch S1 Active * Amazfit Band 7: Add alexa menu entries * Amazfit GTR 3 Pro: Fix firmware and watchface upload * Amazfit T-Rex: Fix activity summary parsing +* Amazfit T-Rex Pro: Add activate display on lift sensitivity * AsteroidOS: Add more supported watch models * AsteroidOS: Fix media info * AsteroidOS: Fix notification dismissal * Bangle.js: Add loyalty cards integration with Catima +* Bangle.js: Ensure SMS messages have src field set to "SMS Message" +* Bangle.js: Fix GPS speed +* Bangle.js: Improve handling of chinese characters * Bangle.js: Lower threshold for low battery warning +* Bangle.js: Recover from device initialization failure * Casio GBX100/GBD-200: Fix first connect +* Casio GB5600/6900/STB-1000: Fix pairing +* Casio GDB-200: Fix notification timestamp +* Casio GDB-200: Fixed notification categories and default category +* Casio GDB-200: Allow preview of notification message alongside title +* Casio GDB-200: Fixed find my phone feature +* Intent API: Add debug action for test new function +* Fossil/Skagen Hybrids: Add new navigation app +* Fossil/Skagen Hybrids: Allow configuring call rejection method * Fossil/Skagen Hybrids: Fix some preference crashes on the nightly * Fossil/Skagen Hybrids: Reduce toasts on release builds +* Fossil/Skagen Hybrids: Show device specific settings in more logical order +* Huami: Toggle phone silent mode from band * Message privacy: Add mode Hide only body * Mijia LYWSD02: Add battery +* Mijia LYWSD02: Add low battery notification * Mijia LYWSD02: Set temperature unit +* Mijia LYWSD02: Fix battery drain while connected * PineTime: Display app name for VoIP app calls +* PineTime: Honor Sync time setting on connect +* PineTime: Improve notification handling * PineTime: Reduce weather memory usage * Withings Steel HR: Fix crash when calibrating hands on the nightly +* Zepp OS: Add blood oxygen graph * Zepp OS: Add workout codes for hiking and outdoor swimming +* Zepp OS: Allow disabling app notifications per device +* Zepp OS: Attempt to fix activity fetch operation getting stuck +* Zepp OS: Display swimming activity data * Zepp OS: Fix health settings on older Zepp OS versions * Zepp OS: Fix setting of unknown button press apps +* Zepp OS: Fix sunrise and moon dates being off by local time + UTC offset * Zepp OS: Map hiking, outdoor swimming, climbing and table tennis activity types -* Add transliteration for Latvian and Common Symbols +* Zepp OS: Toggle phone silent mode from band +* Add transliteration for Latvian, Hungarian, Common Symbols +* Allow multiple device actions to be triggered for the same event +* Allow toggling DND through device actions +* Autodetect OsmAnd package name and make it configurable +* Improve ASCII transliterator +* Make GMaps navigation handler follow the "navigation forwarding" setting +* Support selecting enabled navigation apps * Allow ignore notifications from work profile apps +* Display alias in low battery notification +* Fix crash when pairing current device as companion * Fix emoji when a transliterator is enabled +* Fix UV Index and rain probability for some weather apps * Improve device discovery stability and fix freezes * Improve Telegram and COL Reminder notifications * Replace old-style preference switch with Material 3 switch diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 732608c8c..62a05ff0a 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -24,89 +24,143 @@ * Andreas Shimokawa * Carsten Pfeiffer +* JosÊ Rebelo * Daniele Gobbetti -* Daniel Dakhno * Petr Vaněk * Yaron Shahrabani -* Allan Nordhøy -* Taavi Eomäe +* Daniel Dakhno * 陈少丞 -* Rafael Fontenelle -* JoaĖƒo Paulo Barraca -* Sebastian Kranz +* Arjan Schrijver +* Vincèn PUJOL +* Oğuz Ersen +* Allan Nordhøy +* Ihor Hordiichuk * nautilusx +* Taavi Eomäe +* Gordon Williams +* Rafael Fontenelle +* Michal L +* Sebastian Kranz +* JoaĖƒo Paulo Barraca +* Linerly +* Rex_sa * mamucho * postsorino -* Oğuz Ersen -* FransM +* Manuel-Senpai * Andreas BÃļhler +* FransM * Jonas +* HenRy * Yukai Li * Roi Greenberg -* HenRy +* gallegonovato +* Nikita Epifanov +* kirill blaze +* Óscar FernÃĄndez Díaz +* Jeannette L * Vadim Kaushan * protomors * Cre3per -* Michal L -* JosÊ Rebelo -* Vincèn PUJOL -* Nikita Epifanov +* Davis Mosenkovs +* ssantos * Michael +* glemco * 115ek +* 0que <0que@users.noreply.hosted.weblate.org> +* ĐĄĐ°ŅˆĐ° ПĐĩŅ‚Ņ€ĐžĐ˛Đ¸Ņ› * naofum -* Gordon Williams +* My Random Thoughts +* Damien 'Psolyca' Gaignon +* 0eoc <0eoc@users.noreply.hosted.weblate.org> * mesnevi -* Jeannette L +* Kintu * youzhiran <2668760098@qq.com> * mueller-ma * ivanovlev * Tijl Schepens -* ssantos * Sophanimus * Pavel Elagin +* NekoBox +* MPeter <> +* MrYoranimo * mondstern * HadriÃĄn Candela +* Ács ZoltÃĄn * Zhong Jianxin -* Kintu +* Milo Ivir +* Gabriele Monaco +* foxstidious +* Andy Yang * Abdullah Manaz +* Richard de Boer * mkusnierz <> * Julien Pivotto +* tomechio * Steffen Liebergeld +* Skrripy +* Petr Kadlec +* Pavel * Lem Dulfo +* Dmitriy Bogdanov +* Olexandr Nesterenko * Nevena Mircheva +* musover * Matthieu Baerts -* J. Lavoie * Felix Konstantin Maurer -* Andy Yang +* Axus Wizix +* Xtremo3 * Utsob Roy * taras3333 * Sergey Trofimov +* Sebastian Krey +* Noodlez * M. Hadi +* Martin Boonk +* Lukas +* Ganblejs +* Deixondit +* akasaka / Genjitsu Labs * Szylu * Robert Barat -* Pavel +* Reza Almanda * Mario * ksiwczynski * JohnnySun * Gilles Émilien MOREL -* Deixondit +* firekonstantin +* bruh * Uwe Hermann +* Patric Gruber * opavlov -* Olexandr Nesterenko +* Michalis +* Mario Rossi +* ifurther * Edoardo Rosa -* Dmitriy Bogdanov +* d * BoÅŧydar * Alberto +* AiLab * zsolt3991 +* winver * Vladislav Serkov * Vebryn +* uli * Ted Stein +* sinore +* Shimon +* Reiner Herrmann * NicoBuntu +* Nee Sorry +* Marc Nause * Louis-Marie Croisez +* KryÅĄtof ČernÃŊ +* Johannes Krude * Jean-François Greffier +* Hasan Ammar * Giuseppe Caliendo * Gergely Peidl * Fabio Parri +* Evo * Emre * Elwood * Dmitry Markin @@ -118,153 +172,236 @@ * 0nse <0nse@users.noreply.github.com> * МаĐēŅĐ¸Đŧ Đ¯ĐēиĐŧŅ‡ŅƒĐē * Ye Wint Htut Kyaw +* xaos +* Thomas +* TheScientistPT * SnowCat +* Sergio Varela * Sebastian Obrusiewicz +* Sebastian Espinosa +* Robbert Gurdeep Singh * Rimas RaguliÅĢnas +* mvn23 * Minori Hiraoka (미노ëĻŦ) +* MASVA * masakoodaa * Marius Cornescu -* Mario Rossi +* mantas-p * Lukas Veneziano * LL +* LizardWithHat +* Lesur Frederic * leela <53352@protonmail.com> +* kukuruzka * Kompact +* Kalle * K0L0B0G * Johann C. Rode +* jimman2003 * jfgreffier * Jasper +* ITCactus +* illis * Francesco Marinucci +* FintasticMan +* Doma Gergő * Dikay900 * Denis * Christian Fischer +* Benjamin Swartley * Asbesbopispa -* AiLab * Adolfo Jayme Barrientos * 6arms1leg +* Your Name * XqweX * walkjivefly * WaldiS * Vytenis * Vladislav Glinsky * vishnu -* Thomas -* Sebastian Espinosa +* Vianney le ClÊment de Saint-Marcq +* Toby Murray +* thyttan <6uuxstm66@mozmail.com> +* Thorsten +* Stephan Lachnit +* Sebastian Reichel * Saul Nunez * Rui Mendes +* roolx +* rarder44 +* rany * Ranved Sticon * Rajesh Kumbhakar +* Ptilopsis Leucotis * petronovak -* Petr Kadlec * Pascal * odavo32nof +* octospacc * NotAFIle * Normano64 +* Nikolay Korotkiy * Nick Spacek -* Nee Sorry +* Nekromanser * Nathan +* narektor * MyTimeKill <26295589+MyTimeKill@users.noreply.github.com> * MolnÃĄr BarnabÃĄs * Moarc * Michal Novotny +* maxvel +* Maxime Reyrolle * Mattias MÃŧnster * Mattherix * Martin * marco.altomonte -* LizardWithHat * Le Poisson Libre +* Krzysztof Marcinek * krzys_h * Konrad Iturbe +* Kamalei Zestri <38802353+KamaleiZestri@users.noreply.github.com> +* Joel Beckmeyer * JesÃēs * JesÃēs F * Irul -* ifurther +* Igor Polyakov * homocomputeris +* Grzegorz +* GeekosaurusR3x * Francesco Franchina * fparri +* Fabien Brachere * exit-failure +* Ertu (Er2, Err) +* Er2 * Dreamwalker +* DAWID * Dario Lopez-Kästen * Da Pa * DanialHanif * Cristian Alfano * criogenic * chabotsi +* bowornsin * Avamander +* Artem * AnthonyDiGirolamo * Anonymous * Andreas Kromke +* Alex +* Albert * Ainārs +* ؚبداŲ„ØąØĻŲˆŲ ؚابدی +* ЕĐŗĐžŅ€ ЕŅ€ĐŧĐ°ĐēОв * Ⲇⲁⲛⲓ ÎĻi -* Your Name +* Yusuf Cihan +* yk * Yar * xzovy * xphnx +* XosÊ M +* Xeoy * Xavier RENE-CORAIL -* xaos +* x29a * w2q * Vitaliy Shuruta * veecue * Unixware +* TylerWilliamson * Triet Pham +* Traladarer * Tomer Rosenfeld * Tomas Radej -* Toby Murray * t-m-w * tiparega <11555126+tiparega@users.noreply.github.com> * TinfoilSubmarine +* Tim +* thirschbuechler * Thiago Rodrigues +* thermatk +* theghostofheathledger +* Temuri Doghonadze * Tarik Sekmen * Szymon Tomasz Stefanek * szilardx <15869670+szilardx@users.noreply.github.com> * Swann Martinet +* Stefan Bora * Stan Gomin +* ssilverr +* Sky233ml * SinMan +* Simon Sievert * Sergio Lopez +* Sergey Vasilyev +* sedy89 +* Sebastian Nilsson * S Dantas * Santiago BenalcÃĄzar * Samuel Carvalho de AraÃējo * Sami Alaoui <4ndroidgeek@gmail.com> +* Saman rsh +* Salif Mehmed +* SalavatR * Roxystar * Roman Plevka +* rom4nik +* Robin Davidsson +* Roberto P. Rubio * rober * Rivo Zängov * rimasx +* rikka356 * Richard Finegold * Retew * redking +* Ray +* RandomItalianGuy +* Raghd Hamzeh +* Quang Ngô * Quallenauge * Q-er <9142398+Q-er@users.noreply.github.com> +* pommes +* pishite * Perflyst * Pavel Motyrev * Pauli Salmenrinne * pangwalla * Pander +* ozkanpakdil +* opcode * Ondřej SedlÃĄÄek * Olivier Bloch +* Oleg Vasilev +* Oleg * Nur Aiman Fadel * Nikolai Sinyov * NicolÃ˛ Balzarotti * Nephiel +* Nathan Philipp Bo Seddig * Natanael Arndt * Nabil BENDAFI +* myxor +* Morten Rieger Hannemose * Mirko Covizzi -* Milo Ivir +* Milan Å alka * Mike van Rossum +* mika laka * Michal Novak +* Michael Wiesinger * michaelneu -* Lesur Frederic +* MedusasSphinx * McSym28 * MaxL * maxirnilian * Maxim Baz +* Mave95 * Matej Drobnič * Marvin D * Martin Piatka +* Martin.JM <> * Margreet * Marc Schlaich * Marco Alberto Diosdado Nava * Marco A <35718078+TomasCartman@users.noreply.github.com> -* Marc Nause * Marc Laporte * Marcin * Marcel pl (m4rcel) @@ -274,87 +411,142 @@ * magimel.francois * Maciej Kuśnierz <> * m4sk1n +* LukasEdl +* LuK1337 * Luiz Felipe das Neves Lopes * Luis zas +* Ludovic Jozeau * luca sain * lucanomax +* Liao junchao +* Leon Omelan * Leonardo Amaral * Leo bonilla +* LeJun * Lejun * lazarosfs +* Lars Vogdt * ladbsoft <30509719+ladbsoft@users.noreply.github.com> +* Kyaw Min Khant +* KrisztiÃĄn GÃĄncs <990024@gmail.com> * Kristjan Räts +* KornÊl Schmidt +* kirk1984 +* kieranc001 * kevlarcade * Kevin Richter +* Kevin MacMartin * keeshii * Kaz Wolfe * Kasha * kalaee +* Julien Winning * Julian Lam * jugendhacker * Joseph Kim * jonnsoft <> * Johannes Tysiak -* Joan Perals +* Jochen S +* joaquim.org +* jhey * JF +* Jean-François Milants * jcrode <46062294+jcrode@users.noreply.github.com> * Jan Lolek * Jakub Jelínek +* Jacque Fresco * Izzy * iwonder * Ivan -* Igor Polyakov +* InternalErrorX * HÃŧseyin Aslan +* Hugel * hr-sales * Hirnchirurg -* Hasan Ammar +* Hen Ry +* HelloCodeberg * HardLight * Hanhan Husna +* halemmerich * hackoder +* Gustavo Ramires +* gsbhat <> * Grzegorz Dznsk +* Golbinex <2061409-Golbinex@users.noreply.gitlab.com> +* gnufella +* gnu-ewm * Gleb Chekushin * Giuseppe * GideÃŖo Gomes Ferreira +* gfwilliams * GabO * Gabe Schrecker * freezed-or-frozen * Frank Slezak +* Frank Ertl * Florian Beuscher +* Fabian Hof * Étienne Deparis * EstÊbastien Robespi +* Ernst +* Enrico Brambilla * Edoardo Tronconi * Dougal19 <4662351+Dougal19@users.noreply.github.com> +* Donato * Dmytro Bielik +* djurik * DerFetzer +* Dean <3114661520@qq.com> * Deactivated Account -* Davis Mosenkovs +* David GirÃŗn +* Davide Corradini +* Daniel Thompson * Daniel Hauck +* Dam BOND +* ė´ė •íŦ +* Dachi G +* C * cokecodecock +* CodeSpoof * C O * clach04 * Chris Perelstein +* chklump +* CÊdric Bellegarde * Carlos Ferreira * C0rn3j * ButterflyOfFire * bucala * boun +* BobIsMyManager +* Bilel MEDIMEGH * Benjamin Kahlau +* Ben +* beardhatcode * batataspt@gmail.com * atkyritsis -* Artem +* Ascense +* Aprilhoomie * apre +* Ann Test * Aniruddha Adhikary * angelpup +* Anemograph * Andrzej Surowiec +* Andrew Watkins * andrewlytvyn * AndrewH <36428679+andrewheadricke@users.noreply.github.com> * andre +* Andrea Lepori * Allen B <28495335+Allen-B1@users.noreply.github.com> +* Alicia Hormann * Alfeu Lucas Guedes dos Santos -* Alex * Alexey Afanasev * Alexandra Sevostyanova +* Aidan Crane * aerowolf +* Adam BÃŧchner +* a b <65567823+abb128@users.noreply.github.com> And all the former Transifex translators, who cannot be listed automatically. diff --git a/FEATURES.md b/FEATURES.md deleted file mode 100644 index 636664a4c..000000000 --- a/FEATURES.md +++ /dev/null @@ -1,35 +0,0 @@ -## Feature Matrix - -| | Pebble OG | Pebble Time/2 | Mi Band | Mi Band 2 | Mi Band 3 | Mi Band 4/5 | Amazfit Bip | Amazfit Cor | -|-----------------------------------| ----------|---------------|---------|-----------|-----------|-------------|-------------|-------------| -|Calls Notification | YES | YES | YES | YES | YES | YES | YES | YES | -|Reject Calls | YES | YES | NO | NO | YES | YES | YES | YES | -|Accept Calls | NO(2) | NO(2) | NO | NO | NO | NO | NO | NO | -|Generic Notification | YES | YES | YES | YES | YES | YES | YES | YES | -|Dismiss Notifications on Phone | YES | YES | NO | NO | NO | NO | NO | NO | -|Predefined Replies | YES | YES | NO | NO | NO | NO | NO | NO | -|Voice Replies | N/A | NO(3) | N/A | N/A | N/A | N/A | N/A | N/A | -|Calendar Sync | YES | YES | NO | NO | NO | NO | NO(3) | NO | -|Configure alarms from Gadgetbridge | NO | NO | YES | YES | YES | YES(1) | YES | YES | -|Smart alarms | NO(1) | YES | YES | NO | NO | NO | NO | NO | -|Weather | NO(1) | YES | NO | NO | YES | YES | YES | YES | -|Activity Tracking | NO(1) | YES | YES | YES | YES | YES | YES | YES | -|GPS tracks import | NO | NO | NO | NO | NO | NO | YES | NO | -|Sleep Tracking | NO(1) | YES | YES | YES | YES | YES | YES | YES | -|HR Tracking | N/A | YES | YES | YES | YES | YES | YES | YES | -|Realtime Activity Tracking | NO | NO | YES | YES | YES | YES | YES | YES | -|Music Control | YES | YES | NO | NO | NO | YES | NO | YES | -|Watchapp/face Installation | YES | YES | NO | NO | NO | YES | YES | YES | -|Firmware Installation | YES | YES | YES | YES | YES | YES | YES | YES | -|Taking Screenshots | YES | YES | NO | NO | NO | NO | NO | NO | -|Support Android Companion Apps | YES | YES | NO | NO | NO | NO | NO | NO | - -(1) Possible via 3rd Party Watchapp -(2) Theoretically possible (works on iOS, would need lot of work) -(3) Possible but not implemented yet - - -### Notes about Pebble Firmware >=3.0 - -* Gadgetbridge will keep track of installed watchfaces, but if the Pebble is used with another phone or another app, the information displayed in the app manager can get out of sync since it is impossible to query Firmware >= 3.x for installed apps/watchfaces. - diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index 2a2acbc52..3fcec24ef 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -45,7 +45,7 @@ public class GBDaoGenerator { public static void main(String[] args) throws Exception { - final Schema schema = new Schema(60, MAIN_PACKAGE + ".entities"); + final Schema schema = new Schema(70, MAIN_PACKAGE + ".entities"); Entity userAttributes = addUserAttributes(schema); Entity user = addUserInfo(schema, userAttributes); @@ -70,6 +70,18 @@ public class GBDaoGenerator { addHuamiHeartRateRestingSample(schema, user, device); addHuamiPaiSample(schema, user, device); addHuamiSleepRespiratoryRateSample(schema, user, device); + addXiaomiActivitySample(schema, user, device); + addXiaomiSleepTimeSamples(schema, user, device); + addXiaomiSleepStageSamples(schema, user, device); + addXiaomiManualSamples(schema, user, device); + addXiaomiDailySummarySamples(schema, user, device); + addCmfActivitySample(schema, user, device); + addCmfStressSample(schema, user, device); + addCmfSpo2Sample(schema, user, device); + addCmfSleepSessionSample(schema, user, device); + addCmfSleepStageSample(schema, user, device); + addCmfHeartRateSample(schema, user, device); + addCmfWorkoutGpsSample(schema, user, device); addPebbleHealthActivitySample(schema, user, device); addPebbleHealthActivityKindOverlay(schema, user, device); addPebbleMisfitActivitySample(schema, user, device); @@ -105,6 +117,12 @@ public class GBDaoGenerator { addWena3StressSample(schema, user, device); addFemometerVinca2TemperatureSample(schema, user, device); + addHuaweiActivitySample(schema, user, device); + + Entity huaweiWorkoutSummary = addHuaweiWorkoutSummarySample(schema, user, device); + addHuaweiWorkoutDataSample(schema, user, device, huaweiWorkoutSummary); + addHuaweiWorkoutPaceSample(schema, user, device, huaweiWorkoutSummary); + addCalendarSyncState(schema, device); addAlarms(schema, user, device); addReminders(schema, user, device); @@ -203,7 +221,8 @@ public class GBDaoGenerator { device.addStringProperty("name").notNull(); device.addStringProperty("manufacturer").notNull(); device.addStringProperty("identifier").notNull().unique().javaDocGetterAndSetter("The fixed identifier, i.e. MAC address of the device."); - device.addIntProperty("type").notNull().javaDocGetterAndSetter("The DeviceType key, i.e. the GBDevice's type."); + device.addIntProperty("type").notNull().javaDocGetterAndSetter("The DeviceType key, i.e. the GBDevice's type.").codeBeforeGetterAndSetter("@Deprecated"); + device.addStringProperty("typeName").notNull().javaDocGetterAndSetter("The DeviceType enum name, for example SONY_WH_1000XM3"); device.addStringProperty("model").javaDocGetterAndSetter("An optional model, further specifying the kind of device."); device.addStringProperty("alias"); device.addStringProperty("parentFolder").javaDocGetterAndSetter("Folder name containing this device."); @@ -263,7 +282,7 @@ public class GBDaoGenerator { private static Entity addHuamiStressSample(Schema schema, Entity user, Entity device) { Entity stressSample = addEntity(schema, "HuamiStressSample"); addCommonTimeSampleProperties("AbstractStressSample", stressSample, user, device); - stressSample.addIntProperty("typeNum").notNull().codeBeforeGetterAndSetter(OVERRIDE); + stressSample.addIntProperty("typeNum").notNull().codeBeforeGetter(OVERRIDE); stressSample.addIntProperty("stress").notNull().codeBeforeGetter(OVERRIDE); return stressSample; } @@ -271,7 +290,7 @@ public class GBDaoGenerator { private static Entity addHuamiSpo2Sample(Schema schema, Entity user, Entity device) { Entity spo2sample = addEntity(schema, "HuamiSpo2Sample"); addCommonTimeSampleProperties("AbstractSpo2Sample", spo2sample, user, device); - spo2sample.addIntProperty("typeNum").notNull().codeBeforeGetterAndSetter(OVERRIDE); + spo2sample.addIntProperty("typeNum").notNull().codeBeforeGetter(OVERRIDE); spo2sample.addIntProperty("spo2").notNull().codeBeforeGetter(OVERRIDE); return spo2sample; } @@ -323,6 +342,136 @@ public class GBDaoGenerator { return sleepRespiratoryRateSample; } + private static Entity addXiaomiActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "XiaomiActivitySample"); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.implementsSerializable(); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + activitySample.addIntProperty("stress"); + activitySample.addIntProperty("spo2"); + return activitySample; + } + + private static Entity addXiaomiSleepTimeSamples(Schema schema, Entity user, Entity device) { + Entity sample = addEntity(schema, "XiaomiSleepTimeSample"); + addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device); + sample.addLongProperty("wakeupTime"); + sample.addBooleanProperty("isAwake"); + sample.addIntProperty("totalDuration"); + sample.addIntProperty("deepSleepDuration"); + sample.addIntProperty("lightSleepDuration"); + sample.addIntProperty("remSleepDuration"); + sample.addIntProperty("awakeDuration"); + return sample; + } + + private static Entity addXiaomiSleepStageSamples(Schema schema, Entity user, Entity device) { + Entity sample = addEntity(schema, "XiaomiSleepStageSample"); + addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device); + sample.addIntProperty("stage"); + return sample; + } + + private static Entity addXiaomiManualSamples(Schema schema, Entity user, Entity device) { + Entity sample = addEntity(schema, "XiaomiManualSample"); + addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device); + sample.addIntProperty("type"); + sample.addIntProperty("value"); + return sample; + } + + private static Entity addXiaomiDailySummarySamples(Schema schema, Entity user, Entity device) { + Entity sample = addEntity(schema, "XiaomiDailySummarySample"); + addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device); + sample.addIntProperty("timezone"); + sample.addIntProperty("steps"); + sample.addIntProperty("hrResting"); + sample.addIntProperty("hrMax"); + sample.addIntProperty("hrMaxTs"); + sample.addIntProperty("hrMin"); + sample.addIntProperty("hrMinTs"); + sample.addIntProperty("hrAvg"); + sample.addIntProperty("stressAvg"); + sample.addIntProperty("stressMax"); + sample.addIntProperty("stressMin"); + sample.addIntProperty("standing"); + sample.addIntProperty("calories"); + sample.addIntProperty("spo2Max"); + sample.addIntProperty("spo2MaxTs"); + sample.addIntProperty("spo2Min"); + sample.addIntProperty("spo2MinTs"); + sample.addIntProperty("spo2Avg"); + sample.addIntProperty("trainingLoadDay"); + sample.addIntProperty("trainingLoadWeek"); + sample.addIntProperty("trainingLoadLevel"); + sample.addIntProperty("vitalityIncreaseLight"); + sample.addIntProperty("vitalityIncreaseModerate"); + sample.addIntProperty("vitalityIncreaseHigh"); + sample.addIntProperty("vitalityCurrent"); + return sample; + } + + private static Entity addCmfActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "CmfActivitySample"); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.implementsSerializable(); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + activitySample.addIntProperty("distance"); + activitySample.addIntProperty("calories"); + return activitySample; + } + + private static Entity addCmfStressSample(Schema schema, Entity user, Entity device) { + Entity stressSample = addEntity(schema, "CmfStressSample"); + addCommonTimeSampleProperties("AbstractStressSample", stressSample, user, device); + stressSample.addIntProperty("stress").notNull().codeBeforeGetter(OVERRIDE); + return stressSample; + } + + private static Entity addCmfSpo2Sample(Schema schema, Entity user, Entity device) { + Entity spo2sample = addEntity(schema, "CmfSpo2Sample"); + addCommonTimeSampleProperties("AbstractSpo2Sample", spo2sample, user, device); + spo2sample.addIntProperty("spo2").notNull().codeBeforeGetter(OVERRIDE); + return spo2sample; + } + + private static Entity addCmfSleepSessionSample(Schema schema, Entity user, Entity device) { + Entity sleepSessionSample = addEntity(schema, "CmfSleepSessionSample"); + addCommonTimeSampleProperties("AbstractTimeSample", sleepSessionSample, user, device); + sleepSessionSample.addLongProperty("wakeupTime"); + sleepSessionSample.addByteArrayProperty("metadata"); + return sleepSessionSample; + } + + private static Entity addCmfSleepStageSample(Schema schema, Entity user, Entity device) { + Entity sleepStageSample = addEntity(schema, "CmfSleepStageSample"); + addCommonTimeSampleProperties("AbstractTimeSample", sleepStageSample, user, device); + sleepStageSample.addIntProperty("duration").notNull(); + sleepStageSample.addIntProperty("stage").notNull(); + return sleepStageSample; + } + + private static Entity addCmfHeartRateSample(Schema schema, Entity user, Entity device) { + Entity heartRateSample = addEntity(schema, "CmfHeartRateSample"); + addCommonTimeSampleProperties("AbstractHeartRateSample", heartRateSample, user, device); + heartRateSample.addIntProperty(SAMPLE_HEART_RATE).notNull().codeBeforeGetter(OVERRIDE); + return heartRateSample; + } + + private static Entity addCmfWorkoutGpsSample(Schema schema, Entity user, Entity device) { + Entity sample = addEntity(schema, "CmfWorkoutGpsSample"); + addCommonTimeSampleProperties("AbstractTimeSample", sample, user, device); + sample.addIntProperty("latitude"); + sample.addIntProperty("longitude"); + return sample; + } + private static void addHeartRateProperties(Entity activitySample) { activitySample.addIntProperty(SAMPLE_HEART_RATE).notNull().codeBeforeGetterAndSetter(OVERRIDE); } @@ -861,6 +1010,7 @@ public class GBDaoGenerator { return activitySample; } + private static Entity addWithingsSteelHRActivitySample(Schema schema, Entity user, Entity device) { Entity activitySample = addEntity(schema, "WithingsSteelHRActivitySample"); activitySample.implementsSerializable(); @@ -896,7 +1046,7 @@ public class GBDaoGenerator { private static Entity addWena3StressSample(Schema schema, Entity user, Entity device) { Entity stressSample = addEntity(schema, "Wena3StressSample"); addCommonTimeSampleProperties("AbstractStressSample", stressSample, user, device); - stressSample.addIntProperty("typeNum").notNull().codeBeforeGetterAndSetter(OVERRIDE); + stressSample.addIntProperty("typeNum").notNull().codeBeforeGetter(OVERRIDE); stressSample.addIntProperty("stress").notNull().codeBeforeGetter(OVERRIDE); return stressSample; } @@ -942,6 +1092,99 @@ public class GBDaoGenerator { return perAppSetting; } + private static Entity addHuaweiActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "HuaweiActivitySample"); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addIntProperty("otherTimestamp").notNull().primaryKey(); + activitySample.addByteProperty("source").notNull().primaryKey(); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty("calories").notNull(); + activitySample.addIntProperty("distance").notNull(); + activitySample.addIntProperty("spo").notNull(); + activitySample.addIntProperty("heartRate").notNull(); + return activitySample; + } + + private static Entity addHuaweiWorkoutSummarySample(Schema schema, Entity user, Entity device) { + Entity workoutSummary = addEntity(schema, "HuaweiWorkoutSummarySample"); + + workoutSummary.setJavaDoc("Contains Huawei Workout Summary samples (one per workout)"); + + workoutSummary.addLongProperty("workoutId").primaryKey().autoincrement(); + + Property deviceId = workoutSummary.addLongProperty("deviceId").notNull().getProperty(); + workoutSummary.addToOne(device, deviceId); + Property userId = workoutSummary.addLongProperty("userId").notNull().getProperty(); + workoutSummary.addToOne(user, userId); + + workoutSummary.addShortProperty("workoutNumber").notNull(); + workoutSummary.addByteProperty("status").notNull(); + workoutSummary.addIntProperty("startTimestamp").notNull(); + workoutSummary.addIntProperty("endTimestamp").notNull(); + workoutSummary.addIntProperty("calories").notNull(); + workoutSummary.addIntProperty("distance").notNull(); + workoutSummary.addIntProperty("stepCount").notNull(); + workoutSummary.addIntProperty("totalTime").notNull(); + workoutSummary.addIntProperty("duration").notNull(); + workoutSummary.addByteProperty("type").notNull(); + workoutSummary.addShortProperty("strokes").notNull(); + workoutSummary.addShortProperty("avgStrokeRate").notNull(); + workoutSummary.addShortProperty("poolLength").notNull(); + workoutSummary.addShortProperty("laps").notNull(); + workoutSummary.addShortProperty("avgSwolf").notNull(); + + workoutSummary.addByteArrayProperty("rawData"); + + return workoutSummary; + } + + private static Entity addHuaweiWorkoutDataSample(Schema schema, Entity user, Entity device, Entity summaryEntity) { + Entity workoutDataSample = addEntity(schema, "HuaweiWorkoutDataSample"); + + workoutDataSample.setJavaDoc("Contains Huawei Workout data samples (multiple per workout)"); + + Property id = workoutDataSample.addLongProperty("workoutId").primaryKey().notNull().getProperty(); + workoutDataSample.addToOne(summaryEntity, id); + + workoutDataSample.addIntProperty("timestamp").notNull().primaryKey(); + workoutDataSample.addByteProperty("heartRate").notNull(); + workoutDataSample.addShortProperty("speed").notNull(); + workoutDataSample.addByteProperty("stepRate").notNull(); + workoutDataSample.addShortProperty("cadence").notNull(); + workoutDataSample.addShortProperty("stepLength").notNull(); + workoutDataSample.addShortProperty("groundContactTime").notNull(); + workoutDataSample.addByteProperty("impact").notNull(); + workoutDataSample.addShortProperty("swingAngle").notNull(); + workoutDataSample.addByteProperty("foreFootLanding").notNull(); + workoutDataSample.addByteProperty("midFootLanding").notNull(); + workoutDataSample.addByteProperty("backFootLanding").notNull(); + workoutDataSample.addByteProperty("eversionAngle").notNull(); + workoutDataSample.addByteProperty("swolf").notNull(); + workoutDataSample.addShortProperty("strokeRate").notNull(); + + workoutDataSample.addByteArrayProperty("dataErrorHex"); + + return workoutDataSample; + } + + private static Entity addHuaweiWorkoutPaceSample(Schema schema, Entity user, Entity device, Entity summaryEntity) { + Entity workoutPaceSample = addEntity(schema, "HuaweiWorkoutPaceSample"); + + workoutPaceSample.setJavaDoc("Contains Huawei Workout pace data samples (one per workout)"); + + Property id = workoutPaceSample.addLongProperty("workoutId").primaryKey().notNull().getProperty(); + workoutPaceSample.addToOne(summaryEntity, id); + + workoutPaceSample.addIntProperty("distance").notNull().primaryKey(); + workoutPaceSample.addByteProperty("type").notNull().primaryKey(); + workoutPaceSample.addIntProperty("pace").notNull(); + workoutPaceSample.addIntProperty("correction").notNull(); + + return workoutPaceSample; + } + private static void addTemperatureProperties(Entity activitySample) { activitySample.addFloatProperty(SAMPLE_TEMPERATURE).notNull().codeBeforeGetter(OVERRIDE); activitySample.addIntProperty(SAMPLE_TEMPERATURE_TYPE).notNull().codeBeforeGetter(OVERRIDE); @@ -953,5 +1196,4 @@ public class GBDaoGenerator { addTemperatureProperties(sample); return sample; } - } diff --git a/README.md b/README.md index a17bfb829..a4d99b2cd 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Pebble, Mi Band, Amazfit Bip and HPlus device (and more) without the vendor's cl and without the need to create an account and transmit any of your data to the vendor's servers. -[Homepage](https://gadgetbridge.org) - [Blog](https://blog.freeyourgadget.org) - [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki) - Mastodon +[Homepage](https://gadgetbridge.org) - [Blog](https://blog.freeyourgadget.org) - Mastodon [![Donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Gadgetbridge/donate) @@ -21,120 +21,25 @@ vendor's servers. ## Code Licenses -* Gadgetbrige is licensed under the AGPLv3 +* Gadgetbridge is licensed under the AGPLv3 * Files in app/src/main/java/net/osmand/ and app/src/main/aidl/net/osmand/ are licensed under the GPLv3 by OsmAnd BV - +* Files in app/src/main/java/org/bouncycastle are licensed under the MIT license by The Legion of the Bouncy Castle Inc. ## Download [Get it on F-Droid](https://f-droid.org/app/nodomain.freeyourgadget.gadgetbridge) -[List of changes](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/master/CHANGELOG.md) +- [Nightly releases](https://freeyourgadget.codeberg.page/fdroid/repo?fingerprint=CD381ECCC465AB324E21BCC335895615E07E70EE11E9FD1DF3C020C5194F00B2) + - Nightly releases are updated more frequently and may be less stable than standard releases, and they are distributed by our F-Droid repository unlike standard releases. +- [List of changes](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/master/CHANGELOG.md) ## Supported Devices -**(WARNING: Some of them WIP and some of them without maintainer)** -- Amazfit - - [Balance](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Balance) [**\[!\]**](#special-pairing-procedures) - - [Band 5](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Band-5), [Band 7](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Band-7) [**\[!\]**](#special-pairing-procedures) - - [Bip](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip) - - [Bip Lite](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-Lite), [Bip S](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-S), [Bip U, Bip U Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-U) [**\[!\]**](#special-pairing-procedures) - - [Bip 3 Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-3-Pro) [**\[!\]**](#special-pairing-procedures) - - [Bip 5 (experimental)](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip-5) [**\[!\]**](#special-pairing-procedures) - - [Cor](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor), [Cor 2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor-2) - - [Falcon (experimental)](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Falcon) [**\[!\]**](#special-pairing-procedures) - - [GTR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR), [GTR 2/2e](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR), [GTR 3](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR-3), [GTR 3 Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR-3-Pro), [GTR 4](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR-4) [**\[!\]**](#special-pairing-procedures) - - [GTR Mini](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR-Mini) [**\[!\]**](#special-pairing-procedures) - - [GTS](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS), [GTS 2/2e](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS), [GTS 3](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS-3), [GTS 4](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS-4), [GTS 4 Mini](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTS-4-Mini) [**\[!\]**](#special-pairing-procedures) - - [Neo](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Neo) [**\[!\]**](#special-pairing-procedures) - - T-Rex, T-Rex Pro, [T-Rex 2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-T-Rex-2) [**\[!\]**](#special-pairing-procedures) - - [T-Rex Ultra (experimental)](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-T-Rex-Ultra) [**\[!\]**](#special-pairing-procedures) - - [Cheetah (Round/Square) (experimental)](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cheetah) [**\[!\]**](#special-pairing-procedures) - - [Cheetah Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cheetah-Pro) [**\[!\]**](#special-pairing-procedures) - - Verge Lite [**\[!\]**](#special-pairing-procedures) - - [X ](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-5) [**\[!\]**](#special-pairing-procedures) -- [Bangle.js](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Bangle.js) -- BFH-16 -- [Casio](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Casio) - - Casio GB-X6900B - - Casio GB-6900B - - Casio GB-5600B - - Casio GW-B5600 - - Casio GMW-B5000 (untested) - - Casio STB-1000 - - Casio GBX-100 - - Casio GBD-100 - - Casio GBD-200 - - Casio GBD-H1000 -- [Femometer Vinca II](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Femometer-Vinca-II) -- [FitPro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/FitPro) -- Fossil - - [Hybrid HR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Fossil-Hybrid-HR) [**\[!\]**](#special-pairing-procedures) - - Q Hybrid -- [Galaxy Buds](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds) - - [Galaxy Buds](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#user-content-galaxy-buds) - - [Galaxy Buds Live](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#user-content-galaxy-buds-live) - - [Galaxy Buds Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#galaxy-buds-pro) - - [Galaxy Buds2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#galaxy-buds2) - - [Galaxy Buds2 Pro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Galaxy-Buds#galaxy-buds2-pro) -- Garmin - - Vívomove HR -- [HPlus Devices (e.g. ZeBand)](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/HPlus) -- ID115 -- [iTag](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/iTag) -- JYou Y5 -- Lefun - - Lefun ID115 Plus - - Other clones: bohemic smart bracelet -- Lenovo - - Watch 9 - - [Watch X (Plus)](https://codeberg.org/mamutcho/Gadgetbridge/wiki) -- Liveview -- Makibes HR3 -- Mi - - [Band, Band 1A, Band 1S](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band), [Band 2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-2), [Band 3](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-3) - - [Band 4](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-4), [Band 5](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-5), [Band 6](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-6) [**\[!\]**](#special-pairing-procedures) - - [Xiaomi Smart Band 7](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-7) [**\[!\]**](#special-pairing-procedures) - - Xiaomi Temperature and Humidity Monitor Clock (LYWSD02/LYWSD02MMC) (partial support) - - Scale 2 (Currently only displays a toast after stepping on the scale) -- [MyKronoz ZeTime](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/MyKronoz-ZeTime) -- NO.1 F1 -- [Nothing Ear (1)](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Nothing-Ear-%281%29) -- [Nut Mini 3, Nut 2 and possibly others](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Nut) -- Pebble - - [Pebble, Steel, Time, Time Steel, Time Round, 2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Pebble) -- PineTime (InfiniTime Firmware) -- Roidmi, Roidmi 3, Mojietu 3 (Bluetooth FM Transmitters) -- [SMA](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/SMA) Q2 (SMA-Q2-OSS Firmware) -- Sony - - [Headphones](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Sony-Headphones) - - LinkBuds S - - WH-1000XM2, WH-1000XM3, WH-1000XM4, WH-1000XM5 - - WF-SP800N - - WF-1000XM3, WF-1000XM4, WF-1000XM5 (experimental) - - [Wena 3](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Sony-Wena-3) -- Teclast H10, H30 -- TLW64 -- Vibratissimo (Experimental) -- Wasp-OS devices -- XWatch (Affordable Chinese Casio-like smartwatches) -- [Zepp E](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-GTR) [**\[!\]**](#special-pairing-procedures) -- [Shell Racing cars](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/SuperCars) (BLE RC car models) -- [Flipper Zero](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Flipper-Zero) (Multi-tool Device for Geeks) -- VESC (BLDC controller VESC) -- UM25 (USB Voltage meter) - - - -### Special Pairing Procedures - -Some Huami / Amazfit / Mi / Zepp devices can only be paired with Gadgetbridge using a secret key that has to be obtained once using the proprietary app with an account. Detailed instructions in the wiki: [Huami Server Pairing](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Huami-Server-Pairing) - -The Fossil Hybrid HR also requires using the proprietary app, but with a more complicated procedure. Details in the wiki: [Hybrid HR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Fossil-Hybrid-HR). +Please see the [Gadgets](https://gadgetbridge.org/gadgets/) page on the website for a complete list of supported devices. ## Features -Please see [FEATURES.md](https://codeberg.org/Freeyourgadget/Gadgetbridge/src/master/FEATURES.md) +Please see the [Features](https://gadgetbridge.org/basics/features/) page on the website. ## Authors ### Core Team (in order of first code contribution) diff --git a/app/build.gradle b/app/build.gradle index 2e33435bb..ed5180e8b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -78,17 +78,19 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } - compileSdkVersion 33 - buildToolsVersion "33.0.0" + + namespace 'nodomain.freeyourgadget.gadgetbridge' defaultConfig { applicationId "nodomain.freeyourgadget.gadgetbridge" + minSdkVersion 21 targetSdkVersion 33 + compileSdk 33 // Note: always bump BOTH versionCode and versionName! - versionName "0.76.1" - versionCode 226 + versionName "0.79.0" + versionCode 229 vectorDrawables.useSupportLibrary = true buildConfigField "String", "GIT_HASH_SHORT", "\"${getGitHashShort()}\"" buildConfigField "boolean", "INTERNET_ACCESS", "false" @@ -112,8 +114,8 @@ android { flavorDimensions "device_type" productFlavors { - main { - // Ensure that when starting from scratch, 'main' is selected, not 'banglejs' + mainline { + // Ensure that when starting from scratch, 'mainline' is selected, not 'banglejs' getIsDefault().set(true) // the default build product flavor dimension "device_type" @@ -162,20 +164,20 @@ android { resValue "string", "about_activity_title", "@string/about_activity_title_main_nightly" resValue "string", "about_description", "@string/about_description_main_nightly" resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_main_nightly" - + debuggable true if (System.getProperty("nightly_store_file") != null) { signingConfig signingConfigs.nightly //this was conflicting with regular debug type (it should not), so it is only available for CI builds: - productFlavors.main.resValue "string", "pebble_content_provider", "com.getpebble.android.provider" - productFlavors.main.resValue "string", "app_name", "@string/application_name_main_nightly" - productFlavors.main.resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_main_nightly" - productFlavors.main.resValue "string", "about_activity_title", "@string/about_activity_title_main_nightly" - productFlavors.main.resValue "string", "about_description", "@string/about_description_main_nightly" - productFlavors.main.resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_main_nightly" - + productFlavors.mainline.resValue "string", "pebble_content_provider", "com.getpebble.android.provider" + productFlavors.mainline.resValue "string", "app_name", "@string/application_name_main_nightly" + productFlavors.mainline.resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_main_nightly" + productFlavors.mainline.resValue "string", "about_activity_title", "@string/about_activity_title_main_nightly" + productFlavors.mainline.resValue "string", "about_description", "@string/about_description_main_nightly" + productFlavors.mainline.resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_main_nightly" + //keep the pebble provider for people who want this. In case of need we can create Banglejs Nopebble productFlavors.banglejs.resValue "string", "pebble_content_provider", "com.getpebble.android.provider" productFlavors.banglejs.resValue "string", "app_name", "@string/application_name_banglejs_nightly" @@ -222,10 +224,9 @@ android { } - - lintOptions { + lint { abortOnError ABORT_ON_CHECK_FAILURE - lintConfig file("${project.rootDir}/app/src/main/lint.xml") + lintConfig file("$rootDir/app/src/main/lint.xml") // If true, generate an HTML report (with issue explanations, sourcecode, etc) htmlReport true // Optional path to report (default will be lint-results.html in the builddir) @@ -251,7 +252,7 @@ dependencies { // testImplementation "ch.qos.logback:logback-core:1.1.3" implementation 'com.android.support.constraint:constraint-layout:2.0.4' testImplementation "junit:junit:4.13.2" - testImplementation "org.mockito:mockito-core:1.10.19" + testImplementation "org.mockito:mockito-core:2.28.2" testImplementation "org.robolectric:robolectric:4.8.2" testImplementation "org.hamcrest:hamcrest-library:1.3" testImplementation "com.google.code.gson:gson:2.8.9" @@ -289,9 +290,15 @@ dependencies { implementation 'com.jaredrummler:colorpicker:1.0.2' // implementation project(":DaoCore") implementation 'com.github.wax911:android-emojify:0.1.7' - implementation 'com.google.protobuf:protobuf-javalite:3.10.0' + implementation 'com.google.protobuf:protobuf-javalite:3.21.7' implementation 'com.android.volley:volley:1.2.1' + // Bouncy Castle is included directly in GB, to avoid pulling the entire dependency + // It's included in the org.bouncycastle.shaded package, to fix conflicts with + // roboelectric + //implementation 'org.bouncycastle:bcpkix-jdk18on:1.76' + //implementation 'org.bouncycastle:bcprov-jdk18on:1.76' + // NON-FOSS dependencies // implementation('androidx.core:core-google-shortcuts:1.0.1') { // exclude group:'com.google.android.gms' @@ -399,7 +406,7 @@ tasks.clean.dependsOn(tasks.cleanGenerated) protobuf { protoc { - artifact = 'com.google.protobuf:protoc:3.10.0' + artifact = 'com.google.protobuf:protoc:3.21.7' } generateProtoTasks { all().each { task -> diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dcda90c38..fe0f9b349 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> Einstellungen Allgemeine Einstellungen - Verbinde, wenn Bluetooth eingeschaltet ist + Verbindung mit Gadgetbridge-Gerät(en) herstellen, wenn Bluetooth eingeschaltet ist Automatisch starten Verbindung automatisch wiederherstellen Bevorzugter Audioplayer @@ -1030,7 +1030,7 @@ Beschriftung bearbeiten Swolf-Index BasishÃļhe - Swolf-Index + SWOLF Individuell ausgewählte Elemente Zu Filter hinzufÃŧgen Alle Geräte @@ -1337,12 +1337,12 @@ Benachrichtigungseinstellungen Pro App einstellen Alle Apps abwählen - Inhalt ausblenden + Alle Inhalte ausblenden Verwende die App-Liste, umâ€Ļ Benachrichtigungen von ausgewählten Apps ablehnen Benachrichtigungen von ausgewählten Apps zulassen Datenschutzmodus fÃŧr Nachrichten - Inhalt anzeigen + Alle Inhalte anzeigen Alle Apps auswählen Liste der Apps Andere @@ -1487,7 +1487,7 @@ Sony WF-SP800N Ausschalten Ausschalten - Sind Sie sicher, dass Sie das Gerät abschalten wollen\? + Bist du sicher, dass du das Gerät abschalten willst? Nicht unterstÃŧtzte Geräte finden Min Schrittlänge Wenn du diese Option aktivierst, werden beim Scannen alle erkannten Bluetooth-Geräte angezeigt. Durch kurzes Antippen werden Gerätename und MAC-Adresse in die Zwischenablage kopiert. Langes DrÃŧcken startet den Dialog \"Testgerät hinzufÃŧgen\". Kann zu Problemen mit einfrierender App fÃŧhren. @@ -1598,13 +1598,13 @@ \n \nAufgrund der Richtlinien des Google Play Stores ist es uns nicht erlaubt, einen Spendenlink in der App selbst einzubauen, aber wenn dir diese App gefällt, ziehe bitte eine Spende Ãŧber die Gadgetbridge-Homepage unten in Erwägung. Gadgetbridge (Nightly) - Gadgetbridge Nightly - Über Gadgetbridge Nightly + Gadgetbridge (Nightly) + Über Gadgetbridge (Nightly) Cloudloser und freier quelloffener Ersatz fÃŧr Closed-Source Android-Gadget-Apps von Anbietern. Nächtliche VerÃļffentlichungen von Gadgetbridge. Es kann nicht installiert werden, wenn du bereits die Gadgetbridge- oder die Pebble-App installiert hast, da es einen Konflikt mit dem Pebble-Anbieter gibt. Nightly GB läuft Gadgetbridge (Nightly, kein Pebble-Anbieter) - Gadgetbridge Nightly kein Pebble - Über Gadgetbridge Nightly kein Pebble + Gadgetbridge (Nightly, kein Pebble) + Über Gadgetbridge (Nightly, kein Pebble) Nightly kein Pebble GB läuft Empfindlichkeit Normal @@ -1824,7 +1824,7 @@ Android-Begleit-App fÃŧr Bangle.js, die auf dem Gadgetbridge-Projekt aufbaut, mit zusätzlichem Internetzugang. \n \nAufgrund der Richtlinien des Google Play Stores ist es uns nicht erlaubt, einen Spendenlink in der App selbst einzubauen, aber wenn dir diese App gefällt, ziehe bitte eine Spende Ãŧber die Gadgetbridge-Homepage unten in Erwägung. - Nightly Bangle.js wird ausgefÃŧhrt + Bangle.js (Nightly) wird ausgefÃŧhrt StÃŧndlicher Klang Die Uhr piept einmal pro Stunde Meine täglichen Schritterfolge! @@ -1889,7 +1889,7 @@ 40 BPM 45 BPM Erinnerung zur Entspannung - SPO2-Alarmschwelle + SpO2-Alarmschwelle 7 Sekunden 8 Sekunden 9 Sekunden @@ -1966,7 +1966,7 @@ Sony WH-1000XM2 Sony WF-1000XM4 Ausgewogen - Dual Band + Doppelband Stromsparendes GPS GPS GPS + BDS @@ -2010,7 +2010,7 @@ Offline Stimme Bildschirm leuchtet immer Ton & Vibration - Single Band + Einzelband GPS + GALILEO Text in Sprache Reagiert beim Drehen des Handgelenks @@ -2385,4 +2385,429 @@ Amazfit T-Rex Ultra Amazfit Falcon Amazfit GTR Mini + FranzÃļsisch (Kanada) + Das Gerät hat nicht genÃŧgend freien Speicherplatz + Spanisch (Spanien) + Englisch (Indien) + Englisch (Australien) + Text an das Gerät senden + FranzÃļsisch (Frankreich) + Spanisch (Mexiko) + Englisch (Vereinigtes KÃļnigreich) + Englisch (USA) + Spanisch (USA) + Bitte Ãŧberarbeite dein Zifferblatt fÃŧr das benutzerdefinierte MenÃŧ + Englisch (Kanada) + Das Einstellungssymbol wird immer am Ende angezeigt + 3 Mal + Qrio abschließen + Kurz + MenÃŧstruktur: %s + Kalorien + Links + Keine LED + Die aktuelle MTU von %1$d ist zu niedrig,bitte aktiviere eine hohe MTU in den Geräteeinstellungen und trenne/schließe das Gerät neu an. + Riiiver + Nur Nachrichtentext ausblenden + Suica Kontostand + Mittel + Aktivitätseinstellungen + Durchgehend + Schrittzähler + Qrio + Mi Band HRX + Aktiviere die neue Erkennungsaktivität, die Probleme bei der Geräteerkennung beheben sollte. Deaktiviere diese Funktion, wenn du bei der Suche nach deinem Gerät oder der Kopplung mit diesem Probleme hast. + Weiß + Aktuelle Zeit + Schwach + Celsius + Rechts + Einfach + Anzeigeeinstellungen + KÃļrperenergie + Zweimal + Das Gerät schaltet sich automatisch nach dem festgelegten Zeitplan aus und wieder ein + Display einschalten, wenn du auf dein Handgelenk schaust + Schnell + Qrio Ãļffnen + Temperaturskala + Einmal + Wena Pay + Neue Entdeckungsaktivität aktivieren + GrÃŧn + Gelb + Sony Wena 3 + Rot + TÃŧrkis + Sirene + Wähle aus, ob das Gerät die Celsius- oder die Fahrenheitskala verwendet. + MenÃŧ-Symbole + Vibration bei Benachrichtigung + Unbegrenzt + 4 Mal + Fahrenheit + Vibrationsstärke + Bezahlen + Wiederholung der Vibration bei Benachrichtigung + Mitte + Farbe der Benachrichtigungs-LED + Edy Kontostand + Warnung + Keine Vibration + Lila + Stark + Dreifach + Blau + Benachrichtigungseinstellungen + Sony WF-1000XM5 + Einstellungen fÃŧr eingehende Anrufe + UngÃŧltiges JSON fÃŧr MenÃŧstruktur + GrÃļßere SchriftgrÃļße + MenÃŧstruktur zurÃŧcksetzen + SchriftgrÃļße in Kalender, Benachrichtigungen usw. vergrÃļßern + Anrufbenachrichtigungen empfangen + Eingehende Anrufvibration + LED-Farbe bei eingehendem Anruf + Wiederholung von eingehender Anrufvibration + Falls abgeschaltet, wirst du nicht Ãŧber eingehende Anrufe auf der Wena benachrichtigt + Lettisch + MenÃŧstruktur entfernt + Startbildschirm-Symbole + Wetter in Statusleiste + Durchschnittliche Schlagrate + KopfhÃļrer + Normaler Modus (60s-90s) + Zepp Pay + Schneller Modus (30s) + Ignoriere Arbeitsprofilbenachrichtigungen + Amazfit Balance + yd + ZÃŧge/min + Messmodus + Präziser Modus (3min) + TrainingsverknÃŧpfungen + Maximale Schlagrate + Bahnlänge + Anzahl ZÃŧge + Apps-VerknÃŧpfungen + Benachrichtigungen von Apps im Arbeitsprofil nicht an die Uhr senden + Thermometer + Häufige Symbole + Femometer Vinca II + Wanderung + Wrestling + Redmi Smart Band Pro + Bereitschaft + Xiaomi Watch Lite + Amazfit Active Edge + Klingeln (Vibration/Ton) bei eingehenden Anrufen + Alarm fÃŧr Mail-Benachrichtigungen + Xiaomi Smart Band 7 Pro + Klatsche in die Hände, um den Bildschirm zu aktivieren" + ColaCao 2021 + ColaCao 2023 + Redmi Watch 3 Active + Redmi Smart Band 2 + Steh-Zeit + Aktiv-Zeit + Sende Benachrichtigungen + Sende App-Benachrichtigungen an das Gerät + Nachrichtenvorschau in Titelleiste anzeigen + Zeigt vom Gerät zugelassene Vorschau der Nachricht im Benachrichtigungstitel an + Alarmton fÃŧr Kalender-Erinnerungen + Alarm (Vibration/Ton) fÃŧr Kalender-Erinnerungen + Klingelton bei eingehenden Anrufen + Alarm (Vibration/Ton) fÃŧr Mail-Benachrichtigungen + Alarm fÃŧr SMS-Benachrichtigungen + Alarm fÃŧr andere Benachrichtigungen + Alarm (Vibration/Ton) fÃŧr Benachrichtigungen in anderen Kategorien + Xiaomi Smart Band 8 + Mijia Temperatur- und Feuchtigkeitssensor 2 + KÃļrperliche Zusammensetzung + Amazfit Active + Zusätzliches Ziel + Redmi Watch 2 Lite + Aufwachen + Der Bildschirm schaltet sich aus, wenn das Mikrofon eine Zeit lang Stille erkannt hat + Informationen zur Aktivität + Das Passwort muss 4-stellig sein und darf nur Zahlen enthalten + Geländelauf + Verbindungstyp erzwingen + Installation des Zifferblatts abgeschlossen + Installation des Zifferblatts fehlgeschlagen + Automatische Pulsmessung aktivieren + Weitläufiger Himmel + Reines Weiß + FÃŧgt abgerundete Rechtecke um Startbildschirmsymbole hinzu + \'HR Menu Companion\" ist wahrscheinlich nicht installiert + 7-Tage-Fortschritt + Täglicher Fortschritt + Vitalitätswert + ReisefÃŧhrer + Telefonstummschaltung + Normal / Vibration + Intelligente Vibration + Bettzeit + Automatisches Ausschalten + Benachrichtigung auf dem Gerät, wenn die Verbindung zu Bluetooth getrennt wird. + Ganztägig + Optionen erzwingen + Intelligenten Alarm erzwingen + Status der Verbindung + UnterstÃŧtzung von \"Bitte-Nicht-StÃļren\" erzwingen + Durchschnittlicher Blutsauerstoff + Telefon suchen deaktivieren, wenn Bitte-Nicht-StÃļren aktiv ist + Ablehnen von Anrufen aktivieren + Automatische SpO2-Messung aktivieren + Datensynchronisierung im Hintergrund + Alarm Einstellungen + Der Akku des Geräts ist zu schwach + Normal / Lautlos + Vibration / Lautlos + Erneutes Klatschen schaltet den Bildschirm aus" + Statistiken abrufen + Temperaturdaten abrufen + Tragemodus + Sternenhimmel + Erlaube Wena, Gadgetbridge in regelmäßigen Abständen aufzufordern, Aktivitätsdaten vom Gerät herunterzuladen + Navigations-App nicht auf der Uhr installiert + Ablehnen + Zeitplan fÃŧr Schlafmodus + Sendet eine Erinnerung und schaltet zur Bettzeit in den Schlafmodus. Zur geplanten Aufwachzeit ertÃļnt der Weckalarm. + Erhalte eine Benachrichtigung, wenn dein Vitalitätswert in den letzten 7 Tagen 30, 60 oder 100 erreicht hat + Erhalte eine Benachrichtigung, wenn du die maximale Anzahl an Vitalitätspunkten fÃŧr den Tag erreicht hast + Armband + Halskette (Halsband) + Kieselsteine (Schuhschnalle) + Dänisch + Alarm (Vibration/Ton) fÃŧr SMS (Textnachrichten) + Lange drÃŧcken + Summer-Intensität + Blitzschlag + Zifferblatt hochladenâ€Ļ + Zifferblatt hochladen + Vibrieren bei neuer Anweisung + In den Vordergrund rÃŧcken + Gerätebezeichnung + Nicht festgelegt + Bist du sicher, dass du \'%1$s\' lÃļschen mÃļchtest? + Nach unten verschieben + Protokoll-Version + Flache Strecke + Annehmen von Anrufen aktivieren + Navigation gestartet, aber die Navigation-App ist nicht auf der Uhr installiert. Bitte installiere sie Ãŧber den App-Manager. + Ob die Navigations-App automatisch in den Vordergrund kommen soll, wenn sie eine Navigationsanweisung erhält + Du kannst versuchen, den Verbindungstyp zu erzwingen, falls dein Gerät nicht auf Gadgetbridge reagiert + Automatisch + Bluetooth-LE + Bitte-Nicht-StÃļren - nur Alarme + Navigations-Apps + Dient zur Auswahl der Version von OsmAnd, mit der eine Verbindung hergestellt werden soll + Einstellungen zur Navigation + OsmAnd Paketname + Bitte alle Widgets auswählen + 1 oben, 2 unten + Unbekanntes Workout - %s + Ausschaltzeit + Bitte-Nicht-StÃļren - Aus + Freie Kombination + Debug-Anfrage + Aktivitätsbildschirm + Verbindung nur mit verbundenen Geräten wiederherstellen + Verbindung nur mit verbundenen Geräten wiederherstellen, statt mit allen Geräten neu zu verbinden + Transparenz + Arbeitsmodus + Eine Debug-Anfrage an das Huawei-Gerät senden + Symbol fÃŧr aktuelle Wetterbedingungen in der oberen linken Ecke des Startbildschirms anzeigen + App-Name in der Benachrichtigung + Bitte-Nicht-StÃļren - Ein + UnterstÃŧtzung von \"Bitte-Nicht-StÃļren\" erzwingen +\nVERWENDUNG AUF EIGENE GEFAHR + App-spezifische Einstellungen fÃŧr Benachrichtigungen + Seriennummer + Statistiken + Meldungen + 2 oben, 1 unten + 2 oben, 2 unten + Widget-Anordnung + 1 Widget + Nach oben verschieben + 2 Widgets + Ignorieren (Stille) + Verbesserte SchlafÃŧberwachung + UnterstÃŧtzung fÃŧr intelligente Alarme erzwingen +\nVERWENDUNG AUF EIGENE GEFAHR + Starte Aktivitätstimer + Anweisungen fÃŧr die Navigation + Ob die Uhr bei jeder neuen oder geänderten Navigationsanweisung vibrieren soll (nur wenn die App im Vordergrund ist) + Konfiguriere das Verhalten der Navigations-App + Manuell + Bitte-Nicht-StÃļren wenn nicht getragen + Widget + Version 2 + Version 3 + Google Maps + Nothing Ear (2) + Leichte aktive GeräuschunterdrÃŧckung + HUAWEI TruSleep â„ĸ + Bitte-Nicht-StÃļren - nur Prioritäten + Honor Band 4 + Honor Band 5 + Honor Band 6 + Honor Band 7 + Huawei Band (AW70) + Huawei Band 6 + Huawei Band 7 + Huawei Band 8 + Huawei Watch GT + Huawei Band 4 (Pro) + Huawei Watch GT 2 (Pro) + Huawei Watch GT 2e + Huawei Talk Band B6 + Huawei Watch GT 3 (Pro) + Laufen + Xiaomi Watch S1 Active + Mi Watch Color Sport + Pixoo + Version 1 + Xiaomi Watch S1 + Xiaomi Watch S3 + Xiaomi Watch S1 Pro + Xiaomi Smart Band 8 Pro + Mijia MHO-C303 + Honor Band 3 + Nothing Ear (Stick) + JSON-MenÃŧstruktur in Gadgetbridge gesetzt + The Ultima + City of Speed + Widget-Bildschirm + Es mÃŧssen mindestens %1$s Widget-Bildschirme vorhanden sein + Das Gerät hat keine freien Plätze fÃŧr Widget-Bildschirme (insgesamt: %1$s) + Überwache deine Schlafqualität und dein Atemmuster in Echtzeit. +\nAnalysiere dein Schlafmuster und diagnostiziere genau 6 Arten von Schlafproblemen. + Doppelt DrÃŧcken + Der Tag beginnt um + Schritt hoch + Schritt runter + Kann bei der korrekten Schlaferkennung helfen. Sofort sichtbar in der Ansicht der täglichen Aktivitäten. + Deaktiviere nicht das Kontrollkästchen fÃŧr das intelligente Aufwecken. + Einige Geräte behaupten fälschlicherweise, dass sie einige Optionen nicht unterstÃŧtzen. Diese Einstellungen kÃļnnen verwendet werden, um sie trotzdem zu aktivieren. +\nVERWENDUNG AUF EIGENE GEFAHR +\nLese auf der Webseite + Aktiviere das Abweisen von Anrufen vom Gerät + Aktiviere die Annahme von Anrufen vom Gerät + Reparieren der Trainingsdaten + Aktiviere nicht das Kontrollkästchen fÃŧr das intelligente Aufwecken. + Kann bei der korrekten Schlaferkennung helfen. Sofort sichtbar in der Ansicht der täglichen Aktivitäten. + Widget-Bildschirm lÃļschen + Erzwinge Standort der Uhr + Dies wird nur nach bestimmten Aktualisierungen geschehen + Schaltflächenaktion + Welche Aktion wird ausgefÃŧhrt wenn ein eingehender Anruf von der Uhr abgelehnt wird + Nur falls beim \"Hochheben Display aktivieren\" aktiviert ist + Fokus + Methode zur Ablehnung von Anrufen + Zeit fortsetzen + 155 Schläge/min + 165 Schläge/min + 175 Schläge/min + 185 Schläge/min + 195 Schläge/min + 205 Schläge/min + Sony WI-SP600N + OsmAnd(+) + Bluetooth-Classic + Alarmschwelle fÃŧr hohe Aktivität der Herzfrequenz + Deaktivieren von Hydrationswarnungen fÃŧr ein bestimmtes Zeitintervall + Karate + Kendo + Weitsprung + Hochsprung + Eishockey + Honor MagicWatch 2 + Trampolin + Speerwurf + CMF Watch Pro + Bauchtanz + Jagen + Erzwinge UnterstÃŧtzung fÃŧr Standort der Uhr +\nBENUTZUNG AUF EIGENE GEFAHR + Outdoor-Wandern + Bergsteigen + Skateboarden + Wintersport + Skilanglauf + Curling + Jetski fahren + Segeln + Bergwandern + Indoor-Laufen + Crosstrainer + Ruderer + FitnessÃŧbungen + Funktionales Training + KÃļrperliches Training + Taekwondo + Fechten + AbkÃŧhlung + VÃļlkerball + Softball + Federball + Angeln + Treppen + Jazz-Tanz + Lateinischer Tanz + Ballett + Rollschuhlaufen + Martial Arts + Tai-Chi + Hula Hoop + Darten + Bogenschießen + Pferdereiten + Schaukeln + Drachensteigen + Lacross + Freies Training + Leichtathletik + Aerobic-Übung + LiegestÃŧtze + Eislaufen + KÃļrper und Geist + Handrad fahren + Andere Tänze + Querfeldeinlauf + KlimmzÃŧge + Billard + Redmi Watch 2 + Ziehgriff + Rich-Design verwenden + Präfix des Benachrichtigungstitels mit dem Namen der Quellanwendung + Endstatus des Weckens ignorieren + Startstatus beim Aufwachen ignorieren + Intelligente Alarmspanne + Widget-Untertyp + Widget-Bildschirm %s + Begleit-App MenÃŧ Ãļffnen + Stepper + Sit-ups + Fitness-Spiele + Beweglichkeit + Kurzhantel + CrossFit + Horizontale Stange + Barren + Cross-Training + Plank + Rollen + Seiltraining + Smith-Gerät + Dynamischer Zyklus + Pickleball + Kartsport + Diskus-Sport + Kabaddi + Australisches Football + Shot + Statusseite bereitstellen \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2327a130a..8661d7963 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -52,7 +52,7 @@ Ajustes Ajustes generales - Conectarse al dispositivo cuando el Bluetooth estÊ activado + Conectarse al (los) dispositivo(s) Gadgetbridge cuando el Bluetooth estÃĄ(n) activado(s) Iniciar automÃĄticamente Reconectar automÃĄticamente Reproductor de audio favorito @@ -1036,13 +1036,13 @@ Pulsaciones km lat/min - km/min + min/km m/s km/s kcal vueltas estilo de nataciÃŗn - índice swolf + SWOLF índice swolf seg p @@ -1053,7 +1053,7 @@ cm m Vueltas - estilo de nataciÃŗn + Estilo de nataciÃŗn Ritmo mÃĄs rÃĄpido Ritmo mÃĄs lento MÃĄximo @@ -1334,8 +1334,8 @@ Marcar todas las aplicaciones Denegar las notificaciones de las aplicaciones seleccionadas Modo de privacidad de los mensajes - Ocultar contenido - Mostrar contenido + Ocultar todo el contenido + Mostrar todo el contenido Lista de aplicaciones Ajustes de las notificaciones Por ajustes de la aplicaciÃŗn @@ -1959,7 +1959,7 @@ Entrenamiento bÃĄsico Categorías de entrenamiento Categorías de entrenamientos para detectar automÃĄticamente - Alertas + Alerta Notificar cuando se detecta un entrenamiento Volumen sonido ambiente izquierdo ReducciÃŗn del ruido @@ -2273,8 +2273,8 @@ PAI al mes +%d %d min - PAI total - Aumento del PAI por día + Total + Aumento del día Instalar Catima Grupos a sincronizar Sincronizar sÃŗlo tarjetas favoritas @@ -2401,4 +2401,324 @@ EspaÃąol (MÊxico) El dispositivo no tiene suficiente espacio libre Enviar un texto al dispositivo + La MTU actual de %1$d es demasiado baja, por favor, active la MTU alta en la configuraciÃŗn del dispositivo y desconecte/conecte de nuevo el dispositivo. + Mi Band HRX + Activa una nueva actividad de detecciÃŗn, que debería solucionar los problemas de detecciÃŗn de dispositivos. Desactivela si tiene problemas al buscar o emparejar el dispositivo. + Activar nueva actividad de detecciÃŗn + Ajustes de llamadas entrantes + Iniciar temporizador activo + El icono de Ajustes siempre aparece al final + 3 veces + Bloquear Qrio + Hora de apagado + Corta + Calorías + Izquierda + Sin LED + Descendente + VibraciÃŗn inteligente + Riiiver + Permitir que el Wena pida periÃŗdicamente a Gadgetbridge que descargue los datos de actividad del dispositivo + Balance Suica + Media + Ajustes de actividad + Continua + NÃēmero de pasos + Qrio + Blanco + Hora actual + SincronizaciÃŗn de datos de actividad en segundo plano + DÊbil + Iconos de la pantalla de inicio + Tiempo en la barra de estado + Celsius + Ascendente + Derecha + Simple + Ajustes de pantalla + Mayor tamaÃąo de letra + Energía corporal + Dos veces + El dispositivo se apagarÃĄ y encenderÃĄ automÃĄticamente en el horario especificado + Enciende la pantalla cuando mira su muÃąeca + Aumenta el tamaÃąo de letra en el calendario, las notificaciones, etc. + Doble pulsaciÃŗn + RÃĄpida + Desbloquear Qrio + Escala de temperatura + Una vez + Recibir notificaciÃŗn de llamadas + Muestra el icono de las condiciones actuales en la esquina superior izquierda de la pantalla de inicio + Pagos de Wena + BotÃŗn de acciÃŗn + Hora de reanudaciÃŗn + VibraciÃŗn de llamada entrante + Verde + Amarillo + Sony Wena 3 + Color del LED de llamada entrante + Rojo + Azul cielo + Sirena + Seleccione si el dispositivo utiliza la escala Celsius o Fahrenheit. + Iconos del menÃē + RepeticiÃŗn de vibraciÃŗn de llamada entrante + NotificaciÃŗn por vibraciÃŗn + Indefinidamente + 4 veces + Fahrenheit + Intensidad de vibraciÃŗn + Si estÃĄ desactivado, no recibirÃĄ notificaciones de las llamadas entrantes en el Wena + PulsaciÃŗn larga + Formas de pago + Orden de las pÃĄginas de estado + Ajustes de alarma + NotificaciÃŗn repetida por vibraciÃŗn + Centro + AÃąade rectÃĄngulos redondeados alrededor de los iconos de la pantalla de inicio + Color del LED de notificaciÃŗn + Apagado automÃĄtico + Balance Edy + Aviso + Margen de alarma inteligente + Utilizar un diseÃąo elegante + El día comienza en + Ajustes de notificaciones por aplicaciÃŗn + Sin vibraciÃŗn + Morado + Fuerte + Triple + Azul + Pantalla de actividad + Ajustes de notificaciÃŗn + Sony WF-1000XM5 + Ocultar sÃŗlo el cuerpo del mensaje + LetÃŗn + Símbolos frecuentes + Ignorar las notificaciones del perfil de trabajo + No enviar las notificaciones de las aplicaciones del perfil al reloj + Modo Normal (entre 60 o 90 segundos) + Modo RÃĄpido (30s) + Amazfit Balance + Modo de mediciÃŗn + Modo Preciso (3min) + Blanco puro + El Ãēltimo + Auriculares + CombinaciÃŗn libre + Forma física + Zepp Pay + Cielo estrellado + Cielo vasto + Atajos para el entrenamiento + RelÃĄmpago + Atajos de las aplicaciones + Guía + PreparaciÃŗn + TermÃŗmetro + Femometer Vinca II + Obtener las estadísticas + Obtener los datos de la temperatura + Ignorar (silencio) + Tasa media de carrera + Velocidad en ciudad + Rechazar + MÊtodo de rechazo de llamadas + Se ha iniciado la navegaciÃŗn pero la aplicaciÃŗn de navegaciÃŗn no estÃĄ instalada en el reloj. Por favor, instÃĄlela desde App Manager. + yarda + serie/min + QuÊ acciÃŗn se realiza cuando se rechaza una llamada entrante desde el reloj + Frecuencia mÃĄxima de carrera + Longitud de la pista + Total de carreras + La aplicaciÃŗn de navegaciÃŗn no estÃĄ instalada en el reloj + Amazfit Active Edge + Promedio de oxígeno en sangre + Amazfit Active + Anteponer al título de la notificaciÃŗn el nombre de la aplicaciÃŗn de origen + Permite seleccionar la versiÃŗn de OsmAnd a la que conectarse + Nombre de la aplicaciÃŗn en la notificaciÃŗn + Nombre del paquete OsmAnd + Aplicaciones de navegaciÃŗn + Preferencias de navegaciÃŗn + Google Maps + OsmAnd(+) + Alerta para otras notificaciones + Alerta de las notificaciones en el calendario + Alerta (vibraciÃŗn/pitido) de llamadas entrantes + Xiaomi Smart Band 8 + Ejecutando + Horario del modo de reposo + Alerta de llamadas entrantes + Hora de acostarse + Alerta de notificaciones por correo electrÃŗnico + Alerta (vibraciÃŗn/pitido) para notificaciones por correo electrÃŗnico + Enfocar + Alerta (vibraciÃŗn/pitido) para notificaciones SMS (mensajes de texto) + Mostrar una vista previa del mensaje en el título + Redmi Watch 3 Active + Alerta (vibraciÃŗn/pitido) para las notificaciones de la agenda + NÃēmero de serie + DanÊs + Despertar + Estadísticas + Alerta (vibraciÃŗn/pitido) para las notificaciones de la otra categoría + Xiaomi Watch Lite + Muestra una vista previa del mensaje en el título de una notificaciÃŗn segÃēn lo permita el dispositivo + Envía un recordatorio y entra en modo reposo a la hora de acostarte. A la hora programada para despertarse, sonarÃĄ la alarma del despertador. + Alerta de notificaciones por SMS + Alertas + Xiaomi Watch S1 Active + Xiaomi Smart Band 7 Pro + Mi Watch Color Sport + Pixoo + Enviar notificaciones de las aplicaciones al dispositivo + Enviar notificaciones + No molestar - Activado + No molestar - Solo alarmas + No molestar - SÃŗlo con prioridad + No fijado + No molestar - Desactivado + Objetivo secundario + tiempo de reposo + Tiempo activo + Avances diarios + Progreso en 7 días + Recibir una notificaciÃŗn cuando su puntuaciÃŗn de vitalidad alcance 30, 60 o 100 en los Ãēltimos 7 días + Nivel de vitalidad + Recibir una notificaciÃŗn cuando alcances el nÃēmero mÃĄximo de puntos de vitalidad del día + Modo silencio del telÊfono + Modo de uso + Pebble (hebilla de zapato) + Normal / VibraciÃŗn + Vibrar / Silencio + Redmi Smart Band 2 + Pulsera (Pulsera) + Collar (tira para el cuello) + Normal / Silencio + Carrera de montaÃąa + DiseÃąo del widget + El dispositivo no tiene espacio libre para pantallas con widgets (espacio total: %1$s) + Entrenamiento desconocido - %s + Subir + ÂŋSeguro que quieres borrar \'%1$s\'? + Senderismo + 2 widgets + Eliminar la pantalla de widgets + 1 superior, 2 inferiores + 2 superiores, 2 inferiores + Debe haber un mínimo de %1$s pantallas + Widget + Pantalla con los widgets + Bajar + 1 widget + Subtipo de widget + Pantalla %s + Por favor, seleccione todos los widgets + 2 superior, 1 inferior + Lucha libre + ColaCao 2023 + ColaCao 2021 + Aplaudir para subir la pantalla" + Si vuelves a aplaudir, se apagarÃĄ la pantalla" + Sensor de temperatura y humedad Mijia 2 + La pantalla se apagarÃĄ despuÊs de que el micrÃŗfono haya detectado un silencio durante un tiempo + Instrucciones de navegaciÃŗn + Configurar el comportamiento de la aplicaciÃŗn de navegaciÃŗn en el reloj + Si la aplicaciÃŗn de navegaciÃŗn debe pasar automÃĄticamente a primer plano cuando recibe una instrucciÃŗn de navegaciÃŗn + Vibrar al recibir una nueva instrucciÃŗn + Pasar a primer plano + Nombre del dispositivo + Si el reloj debe vibrar con cada instrucciÃŗn de navegaciÃŗn nueva o modificada (sÃŗlo cuando la aplicaciÃŗn estÃĄ en primer plano) + Redmi Watch 2 Lite + Estado de la conexiÃŗn + Redmi Smart Band Pro + Nothing Ear (2) + Nothing Ear (Stick) + Transparencia + CancelaciÃŗn activa del ruido ligera + No molestar cuando no lo use + Manual + Forzar la funciÃŗn No molestar + Forzar el soporte de No Molestar. +\nUTILÍCELO BAJO SU PROPIA RESPONSABILIDAD + Ignorar el estado de inicio del despertador + Puede ayudar a detectar correctamente el sueÃąo. Visible inmediatamente en la vista de actividades diarias. + Ignorar estado de fin de despertador + NotificaciÃŗn en el dispositivo cuando se desconecta de BT. + Todo el día + Honor Band 3 + Honor Band 4 + Honor Band 5 + Honor Band 6 + Honor Band 7 + Huawei Band 6 + Huawei Band 7 + Huawei Band 8 + Huawei Watch GT + Huawei Band 4 (Pro) + Huawei Watch GT 2 (Pro) + Huawei Watch GT 2e + Huawei Talk Band B6 + Huawei Watch GT 3 (Pro) + Modo de trabajo + No desmarque la casilla de activaciÃŗn inteligente. + HUAWEI TruSleep â„ĸ + Monitorice la calidad de su sueÃąo y su patrÃŗn respiratorio en tiempo real. +\nAnaliza tus patrones de sueÃąo y diagnostica con precisiÃŗn 6 tipos de problemas de sueÃąo. + Control del sueÃąo mejorado + Permitir aceptar llamadas + Activar la aceptaciÃŗn de llamadas desde el dispositivo + Permitir rechazar llamadas + Activar el rechazo de llamadas desde el dispositivo + Desactivar encontrar mi telÊfono cuando no molestar estÃĄ activo + Activar la mediciÃŗn automÃĄtica del ritmo cardíaco + Activar la mediciÃŗn automÃĄtica de SpO2 + OpciÃŗn obligatoria + Forzar la alarma inteligente + Lugar de desgaste de la fuerza + Soporte de localizaciÃŗn de desgaste forzoso. +\nUSO BAJO SU PROPIA RESPONSABILIDAD + Reparar datos de entrenamiento + Esto sÃŗlo harÃĄ algo despuÊs de ciertas actualizaciones + Enviar una solicitud de depuraciÃŗn al dispositivo Huawei + Solicitud de depuraciÃŗn + Reconectar sÃŗlo a los dispositivos conectados + Reconectarse sÃŗlo a los dispositivos conectados, en lugar de volver a conectarse a todos los dispositivos + SÃŗlo si se activa la visualizaciÃŗn al subir + Huawei Band (AW70) + No marque la casilla de activaciÃŗn inteligente. + Algunos dispositivos afirman falsamente no ser compatibles con algunas opciones. Esta configuraciÃŗn se puede utilizar para habilitarlos de todos modos. +\nUTILÍZALO BAJO TU PROPIA RESPONSABILIDAD +\nLea la wiki + Forzar el soporte de alarmas inteligentes. +\nUSO BAJO SU PROPIA RESPONSABILIDAD + Puede ayudar a detectar correctamente el sueÃąo. Visible inmediatamente en la vista de actividades diarias. + La batería del dispositivo estÃĄ demasiado baja + Intensidad del zumbador + La contraseÃąa debe tener 4 dígitos, utilizando sÃŗlo nÃēmeros + Xiaomi Smart Band 8 Pro + VersiÃŗn 2 + Forzar el tipo de conexiÃŗn + InformaciÃŗn de la actividad + Mijia MHO-C303 + VersiÃŗn 1 + VersiÃŗn 3 + VersiÃŗn del protocolo + Xiaomi Watch S1 + Xiaomi Watch S3 + Xiaomi Watch S1 Pro + Subiendo esfera del relojâ€Ļ + Subiendo esfera del reloj + InstalaciÃŗn de la esfera del reloj completada + La instalaciÃŗn de la esfera del reloj fallÃŗ + AutomÃĄtico + Bluetooth de baja energía + Bluetooth + Puedes intentar forzar el tipo de conexiÃŗn en caso de que tu dispositivo no responda a Gadgetbridge + Ruta llana + Sony WI-SP600N + 155 bpm + 165 bpm + 175 lpm \ No newline at end of file diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index d5a244270..b469c929f 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -7,7 +7,7 @@ Gadgetbridge Lopeta Lahjoita - Ota ruutukaappaus + Ota kuvankaappaus Katkaise yhteys Poista laite Poista %1$s @@ -168,7 +168,7 @@ Etsi %1$s\? Virheenetsintä Ilmoitusasetukset - Yleiset asetukset + Yleiset Kieli Tilapalkin kuvake ja lukitusnäytÃļn ilmoitus näytetään Yhdistä laitteeseen, kun Bluetooth on päällä @@ -176,14 +176,14 @@ Synkronoi aika Gadgetbridge-laitteeseen, kun muodostat yhteyden, ja kun aika tai aikavyÃļhyke muuttuu Android-laitteessa Puhelut Poista valinta kaikista sovelluksista - Ilmoitukset + Yleiset Yhdistä uudelleen automaattisesti Tilapalkin kuvake ja lukitusnäytÃļn ilmoitus ovat piilotettu Toistot Yleinen ilmoitustuki Käytä sovellusluetteloaâ€Ļ â€ĻmyÃļs näytÃļn ollessa päällä - Sovelluskohtaiset asetukset + Sovelluskohtaiset Estä ilmoitukset valituista sovelluksista Ping-ääni Tekstiviesti @@ -211,7 +211,7 @@ Oikealta vasemmalle enimmäisrivin pituus Oikealta vasemmalle Valitse kaikki sovellukset - Estä kaikki ilmoitukset, kun älä häiritse tila on valittuna puhelimessa + Älä lähetä ilmoituksia laitteille, kun Älä häiritse -tila on päällä Salli ilmoitukset valituista sovelluksista Translitterointi Ota tämä käyttÃļÃļn, jos laitteesi ei tue kielesi fonttia @@ -403,7 +403,7 @@ Syke Tietojen hallinta Tunnit: - Kieli- ja alueasetukset + Kieli ja alue Laiteohjelmiston versio: %1$s "SYKE: " Keskisyke @@ -481,7 +481,7 @@ kysy tunnista pyÃļräily Bangle.js Gadgetbridge - Bangle.js on käynnissä + Bangle.js Gadgetbridge on käynnissä Gadgetbridge on käynnissä Valot Bangle.js Gadgetbridge @@ -571,4 +571,26 @@ Vastamelun optimointi Sony WF-1000XM4 YmpäristÃļäänten hallinta + Aika + Gadgetbridge Nightly + Jaa + Kalenteri + Tietoja Bangle.js Gadgetbridgestä + Suosi ilmoitusten pitkää muotoa + Sivuuta matalan prioriteetin ilmoitukset + Älä lähetä matalan prioriteetin ilmoituksia laitteille + Varoitus + Androidin ilmoitusasetukset + Jos saatavilla, lähetä ilmoituksen pitkä muoto laitteisiin + Tietoja Gadgetbridge Nightlystä + Terveys + Tietoja Bangle.js Gadgetbridge Nightlystä + Välimuistita ilmoitukset kun saavuttamattomissa + Bangle.js Gadgetbridge Nightly on käynnissä + Valitse tiedosto + Bangle.js Gadgetbridge Nightly + Laitteiden ollessa saavuttamattomissa välimuistita ilmoitukset ja lähetä ne niille yhteyden palattua + Gadgetbridge Nightly on käynnissä + Bangle.js Gadgetbridge Nightly + Gadgetbridge Nightly \ No newline at end of file diff --git a/app/src/main/res/values-fr-rCA/strings.xml b/app/src/main/res/values-fr-rCA/strings.xml index fdbef6cda..0cffe1aa9 100644 --- a/app/src/main/res/values-fr-rCA/strings.xml +++ b/app/src/main/res/values-fr-rCA/strings.xml @@ -898,4 +898,6 @@ \nÀ VOS RISQUES ET PÉRILS! DÊfinir un alias Rechercher %1$s\? + Alerte pour les notifications de courriel + Alerte (vibre/bipe) pour les notifications de courriel \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d94ab3667..60d898ee9 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -825,7 +825,7 @@ Temps de sommeil prÊfÊrÊ en heures Permet les alertes de calendrier, mÃĒme en cas de dÊconnexion Sync. calendrier des Êvènements FrÊquence cardiaque - Étapes + Pas Date Les minutes actives Calories @@ -927,7 +927,7 @@ Temps de sommeil prÊfÊrÊ en heures Étiquette Filtre Effacer les filtres - A + À De Statistiques sur les activitÊs sportives Filtre sur les activitÊs sportives @@ -968,7 +968,7 @@ Temps de sommeil prÊfÊrÊ en heures Plat Ascendant Type de nage - index swolf + SWOLF Moyenne de pause FoulÊes moyennes Distance moyenne de foulÊe @@ -990,7 +990,7 @@ Temps de sommeil prÊfÊrÊ en heures Erreur lors de la rÊcupÊration des appareils de la base de donnÊes Erreur pour configurer l\'alias: Erreur à l\'exportation des prÊfÊrences spÊcifiques de cet appareil - Demander et vÊrifier les autorisations manquantes mÃĒme si elles ne sont pas immÊdiatement nÊcessaires. Les dÊsactiver uniquement si votre appareil ne supporte pas ces fonctions. Refuser une autorisation peut provoquer des problèmes ! + Demander et vÊrifier les autorisations manquantes mÃĒme si elles ne sont pas immÊdiatement nÊcessaires. DÊsactiver uniquement si votre appareil ne supporte pas ces fonctions. Refuser une autorisation peut provoquer des problèmes ! VÊrifier l\'Êtat des autorisations Le dÊmarrage du service en arrière-plan n\'a pas rÊussi à cause d\'une exception. Erreur: CLÉ NÉCESSAIRE, APPUI LONG POUR ENTRER @@ -1008,8 +1008,8 @@ Temps de sommeil prÊfÊrÊ en heures Contributeurs Équipe principal (dans l\'ordre de la première contribution au code) Un remplaçant libre et sans cloud aux applications Android propriÊtaires des fabricants de vos bracelets. - A propos de Gadgetbridge - A propos + À propos de Gadgetbridge + À propos Horloge mondiale Stress Respiration @@ -1033,7 +1033,7 @@ Temps de sommeil prÊfÊrÊ en heures EntraÃŽneur elliptique VÊlo d\'intÊrieur Natation (en eau libre) - UtilisÊ par le fournisseur mÊtÊo de LineageOS, les autres versions d\'Android doivent utilisÊes une application comme Weather notification. Se reporter au wiki Gadgetbridge. + UtilisÊ par le fournisseur mÊtÊo de LineageOS, les autres versions d\'Android doivent utiliser une application comme Weather notification. Se reporter au wiki Gadgetbridge. Vous ÃĒtes sur le point d\'installer le micrologiciel %s dans votre Mi Band 5. \n \nVeuillez installer le fichier .fw, puis le fichier .res. Votre montre redÊmarrera après avoir installÊ le fichier .fw. @@ -1334,8 +1334,8 @@ Temps de sommeil prÊfÊrÊ en heures Utiliser la liste des applications pourâ€Ļ Bloquer les notifications des apps sÊlectionnÊes Mode messages privÊs - Afficher le contenu - Cacher le contenu + Afficher tout le contenu + Cacher tout le contenu Liste des applications L\'app doit ÃĒtre sÊlectionnÊe pour ÃĒtre configurÊe Autoriser les notifications des apps sÊlectionnÊes @@ -1585,16 +1585,16 @@ Temps de sommeil prÊfÊrÊ en heures S\'allumer pour une nouvelle notification Email Bangle.js Gadgetbridge - A propos de Bangle.js Gadgetbridge + À propos de Bangle.js Gadgetbridge Bangle.js Gadgetbridge Application complÊmentaire Android pour Bangle.js ÊlaborÊe sur la base du projet Gadgetbridge, avec des fonctionnalitÊs Internet supplÊmentaires. \n \nEn raison des règles du Google Play Store, nous ne pouvons pas mettre de lien de don directement dans l\'application, mais si vous apprÊciez cette application, pensez à faire un don via la page web de Gadgetbridge ci-dessous. Bangle.js pour Gadgetbridge Bangle.js pour Gadgetbridge - A propos de Gatgetbridge Nightly + À propos de Gatgetbridge Nightly A propos de Bangle.js pour Gadgetbridge - A propos de Gadgetbridge Nightly sans Pebble + À propos de Gadgetbridge Nightly sans Pebble Remplaçant autonome et sous licence libre pour remplacer les applications propriÊtaires des fabricants. Version Nightly de Gadgetbridge. Ne peut pas ÃĒtre installÊ si vous avez dÊja Gadgetbridge ou Pebble d\'installÊ, en raison d\'un conflit avec l\'application Pebble. Gadgetbridage (Nightly, pas defournisseur Pebble) Gadgetbridge Nightly Sans Pebble @@ -1669,7 +1669,7 @@ Temps de sommeil prÊfÊrÊ en heures Permettre aux apps sur cet appareil d\'accÊder à Internet Permettre les intentions Application de suivi sportif - DÊmmare/arrÃĒte le suivi sportif sur le tÊlÊphone si une activitÊ GPS est dÊmarrÊ sur le bracelet + DÊmarre/arrÃĒte le suivi sportif sur le tÊlÊphone si une activitÊ GPS est dÊmarrÊe sur le bracelet Envoyer le GPS durant l\'exercice Envoyer les donnÊes GPS en cours durant un exercice GPS Gadgebridge @@ -1814,7 +1814,7 @@ Temps de sommeil prÊfÊrÊ en heures ÉvÊnements personnalisÊs CrÊation du fichier journal ratÊe, l\'Êcriture du fichier journal est indisponible. RedÊmarrer l\'application pour essayer de nouveau l\'Êcriture du fichier journal. Obtenir les mesures de rythme cardiaque - A propos de Bangle.js Gadgetbridge (Version quotidienne) + À propos de Bangle.js Gadgetbridge (Version quotidienne) Bangle.js Gadgetbridge (Version quotidienne) Bangle.js Gadgetbridge (Version quotidienne) Capteur binaire @@ -1826,13 +1826,13 @@ Temps de sommeil prÊfÊrÊ en heures La montre fera un beep toutes les heures Objectifs de pas Mes rÊsultats quotidiens de pas ! - %1$s a besoin d\'une autorisation pour s\'afficher au-dessus des autres apps afin de pouvoir lanter des activitÊs via la commande vocale lorsque %1$s est en arrière-plan. + %1$s a besoin d\'une autorisation pour s\'afficher au-dessus des autres apps afin de pouvoir lancer des activitÊs via la commande vocale lorsque %1$s est en arrière-plan. \n \nCela peut ÃĒtre utilisÊ pour dÊmarrer une application de musique et jouer un morceau, et de nombreuses autres choses. \n \nMerci de choisir \'%2$s\' puis \'%1$s\' et activer \'Permettre l\'affichage au-dessus des autres apps\', puis faire \'Retour\' pour revenir à %1$s. \n -\nPour empÃĒcher %1$s de demander des permissions se rendre dans les \'RÊgalges\' et dÊcocher \'VÊrifier l\'Êtat des permissions\'. +\nPour empÃĒcher %1$s de demander des permissions se rendre dans les \'RÊglages\' et dÊcocher \'VÊrifier l\'Êtat des permissions\'. \n \nAssurez-vous d\'accorder à %1$s les permissions nÊcessaires pour les fonctionnalitÊs attendues. Heure @@ -2043,7 +2043,7 @@ Temps de sommeil prÊfÊrÊ en heures Objectif quotidien : temps d\'Êlimination de calories en minutes Mode CinÊma Activer le mode sommeil automatiquement lors du port du bracelet durant le sommeil - Tronquer lse horaires lors de la rÊcupÊration + Tronquer les horaires lors de la rÊcupÊration Soleil & Lune Voix hors-ligne Écran tout le temps allumÊ @@ -2053,8 +2053,8 @@ Temps de sommeil prÊfÊrÊ en heures TÊlÊphone LuminositÊ Action sur un appui court du bouton infÊrieur - liste des applications dont les notifications mÊdia doivent ÃĒtre ignorÊes - Traiter les notifications mÊdia avant la liste des app. Si la prÊfÊrence n\'est pas cochÊe, les applicatioms mÊdia doivent ÃĒtre autorisÊes dans la liste des applications pour que les contrôles mÊdia fonctionnent sur l\'appareil. + Liste des applications dont les notifications mÊdia doivent ÃĒtre ignorÊes + Traiter les notifications mÊdia avant la liste des app. Si la prÊfÊrence n\'est pas cochÊe, les applications mÊdia doivent ÃĒtre autorisÊes dans la liste des applications pour que les contrôles mÊdia fonctionnent sur l\'appareil. Supprimer les prÊfÊrences de l\'appareil \? Cela supprimera toutes les prÊfÊrences appareil pour tous les appareils connectÊs. Etes-vous sÃģr \? Éjecter l\'eau @@ -2167,7 +2167,7 @@ Temps de sommeil prÊfÊrÊ en heures Coupler pour les appels Bluetooth Afin de recevoir les appels en Bluetooth, vous devez pairer votre tÊlÊphone avec une seconde instance de la montre. 2. Rendez-vous dans les rÊglages Bluetooth de votre tÊlÊphone, et pairez-le avec le nouvel appareil qui apparaitra (nom similaire à votre montre existante, mais avec un suffixe, du style \"Amazfit GTR 4 - AFC8\". - Masquer lorsque hors de portÊe + Garder en cache lorsque hors de portÊe Envoyer les notifications manquÊes lorsqu\'un appareil se reconnecte après avoir ÊtÊ hors de portÊe Annulation de bruit ←→ Ambiance ←→ Off Appui simple @@ -2403,5 +2403,190 @@ Temps de sommeil prÊfÊrÊ en heures Français (France) Cet appareil n\'a pas assez d\'espace libre Envoyer un message texte à l\'appareil - Le MTU actuel pour %1$d est trop faible, merci de rÊgler un MTU ÊlevÊ dans les rÊglages de l\'appareil. + Le MTU actuel pour %1$d est trop faible, merci de rÊgler un MTU ÊlevÊ dans les rÊglages de l\'appareil. et puis dÊconnecter/reconnecter l\'appareil. + Mi Band HRX + Activer la dÊcouverte de nouvelles activitÊs + RÊglages pour les appels entrants + DÊmarrer le chronomètre + L\'icône des rÊglages est toujours affichÊe en dernière + 3 fois + Verrouillage Qrio + Heure d\'extinction + Court + Calories + Gauche + Aucune LED + Descente + Vibration intelligente + Riiiver + Cacher uniquement le contenu + Permet à Wena de pÊriodiquement demander à Gadgetbridge de rÊcupÊrer les donnÊes depuis votre appareil + Balance Suica + Moyen + RÊglages d\'activitÊ + Continu + Nombre de pas + Qrio + Activer le nouveau service de dÊcouverte, ce qui devrait rÊsoudre le problème de dÊcouverte. DÊsactiver cette option en cas de problèmes si vous avez des problèmes pour dÊcouvrir ou pairer votre appareil. + Blanc + Heure actuelle + ActivitÊ de synchronisation en tache de fond + Faible + Icônes de l\'Êcran d\'accueil + MÊtoÊ dans la barre d\'Êtat + Celsius + MontÊe + Droite + Basique + RÊglages d\'affichage + Taille de texte plus grande + Énergie corporelle + Deux fois + L\'appareil s\'Êteindra et s\'allumera automatiquement selon les intervalles configurÊs + Allumer l\'Êcran lorsque vous regardez votre poignÊe + Augmenter la taille du texte dans le calendrier, les notifications, etc. + Double appui + Rapide + DÊverrouillage Qrio + Échelle de tempÊrature + Une fois + Notifications pour les appels entrants + Afficher l\'icône des conditions en cours dans le coin supÊrieur gauche de l\'Êcran d\'accueil + Wena Pay + Bouton Action + Heure d\'allumage + Vibration pour les appels entrants + Vert + Jaune + Sony Wena 3 + Couleur de la LED pour les appels entrants + Rouge + Cyan + Sirène + Choisir si l\'appareil utilise Celsius ou Farenheit. + Icônes du Menu + RÊpÊter les vibrations pour les appels entrants + Vibration de notification + Infiniment + 4 fois + Fahrenheit + Force des vibrations + Si Êteint, vous ne serez pas notifiÊ des appels entrants sur le Wena + Appui long + Payement + Letton + Organisation de la page d\'Êtat + RÊglages de l\'alarme + RÊpÊter les vibrations de notification + Centre + Ajoute des rectangles ronds autour des icônes de l\'Êcran d\'accueil + Couleur de la LED de notification + Extinction automatique + Balance Edy + Avertissement + Marge de l\'alarme intelligente + Utiliser Rich Design + Le jour commence à + RÊglages de notification par app + Aucune vibration + Pourpre + Fort + Triple + Bleu + Écran d\'activitÊ + RÊglages de notification + Sony WF-1000XM5 + Symboles communs + Ignorer les notifications en profil travail + Ne pas envoyer les notifications des apps dans le profil travail à la montre + Blanc pur + Ignorer (silence) + Rythme moyen de course + L\'ultima + Vitesse urbaine + Écouteur + Mode normal (60s - 90s) + Rejet + Combinaison libre + Composition corporelle + Zepp Pay + MÊthode de rejet d\'appel + RÊcupÊration des statistiques + Mode Rapide (30s) + Ciel ÊtoilÊ + Balance Amazfit + La navigation a dÊmarrÊe mais pas d\'app de navigation installÊe dans la montre. Merci d\'en installer une depuis le Gestionnaire d\'App. + yard + str/min + MÊthode de mesure + Quelle action à faire quand un appel entrant est rejetÊ depuis la montre + Amazfit Active Edge + RÊcupÊration des donnÊes de tempÊrature + Mode PrÊcis (3 min) + Ciel clair + Raccourcis Exercice + Vitesse maximale de course + Flash lumineux + Longueur de ligne + Moyenne de l\'oxygène dans le sang + Vitesse totale + Raccourcis Apps + Guide + PrÊparation + Amazfit Active + Thermomètre + Femometer Vinca II + App de navigation non installÊe sur le tÊlÊphone + Alerte pour les autres notifications + Alerte pour les notifications du calendrier + Alerte (vibre/bipe) pour les appels entrants + Alerte pour les appels entrants + Alerte pour les notifications d\'email + Alerte (vibre/bipe) pour les notifications d\'email + Alerte (vibre/bipe) pour les notifications SMS (message texte) + Montrer un aperçu du message dans le titre + Alerte (vibre/bipe) pour les notifications du calendrier + Alerte (vibre/bipe) pour les notifications de la catÊgorie autre + Nom de l\'app dans la notification + Alerte pour les notifications SMS + Mi Watch Color Sport + Applications de navigation + Xiaomi Smart Band 8 + Course à pieds + Planification de l\'heure de coucher + Heure du coucher + Pixoo + PrÊfÊrences de navigation + Focus + Google Maps + Ajouter un prÊfixe au titre de la notification avec le nom de l\'application source + Redmi Watch 3 Active + NumÊro de sÊrie + Danois + UtilisÊ pour sÊlectionner la version d\'OsmAnd à laquelle se connecter + RÊveil + Xiaomi Smart Band 7 Pro + Stats + Xiaomi Watch Lite + Afficher un aperçu du message dans le titre de la notification comme permis par l\'appareil + nom du paquet OsmAnd + OsmAnd(+) + Envoyer un rappel et se mettre en veille à l\'heure du coucher. A l\'heure prÊvue de rÊveil, l\'alarme de rÊveil sonnera. + Alertes + Xiaomi Watch S1 Active + Ne pas dÊranger - Actif + Objectif secondaire + Envoyer une notification quand votre score de vitalitÊ atteint 30, 60 ou 100 sur les 7 derniers jours + Score VitalitÊ + Ne pas dÊranger - Alarmes uniquement + Envoyer une notification lorsque vous atteignez le maximum de points quotidiens de vitalitÊ + Envoyer les notifications de l\'application à l\'appareil + Ne pas dÊranger - Prioritaire uniquement + Non configurÊ + Temps de veille + Temps actif + Envoyer des notifications + Progrès quotidien + Progrès sur 7 jours + Ne pas dÊranger - Off \ No newline at end of file diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index cf8a68aef..aca505886 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -1331,12 +1331,12 @@ הגדרו×Ē נפרדו×Ē לכל יישומון יישומון ×Ļריך להיו×Ē לא מסומן כדי שיוגדר יישומון חייב להיו×Ē מסומן כדי שיוגדר - הס×Ēר×Ē ×Ēוכן + הס×Ēר×Ē כל ה×Ēוכן רשימ×Ē יישומונים סימון כל היישומונים להש×Ēמ׊ ברשימ×Ē היישומונים כדיâ€Ļ לדחו×Ē ה×Ēראו×Ē מהיישומונים הנבחרים - ה×Ļג×Ē ×Ēוכן + ה×Ļג×Ē כל ה×Ēוכן הדגש×Ē הקול מ׊אר ה×Ļלילים Galaxy Buds ×ĸרכה של אקוולייזר @@ -2398,4 +2398,100 @@ ספרדי×Ē של ספרד אין מספיק מקום פנוי במכשיר שליח×Ē טקץט למכשיר + הגדר×Ē ה־MTU (יחיד×Ē ה×ĸברה מרבי×Ē) הנוכחי×Ē %1$d נמוכה מדי, נא להפ×ĸיל MTU גבוה בהגדרו×Ē המכשיר ולנ×Ēק ולחבר א×Ē המכשיר שוב. + Mi Band HRX‎ + הפ×ĸל×Ē פ×ĸילו×Ē הגילוי החדשה שאמורה לפ×Ēור ×Ēקלו×Ē בגילוי ה×Ēקנים. ר×Ļוי להשבי×Ē או×Ēה אם ×Ļ×Ļו×Ē ×Ēקלו×Ē במהלך חיפוש אחר או ×Ļימוד הה×Ēקן שלך. + הפ×ĸל×Ē פ×ĸילו×Ē גילוי חדשה + Sony Wena 3‎ + שמאל + לבן + ימין + הגדרו×Ē ×Ē×Ļוגה + ירוק + ×Ļהוב + אדום + ×Ļיאן + מרכז + סגול + כחול + הגדרו×Ē שיחו×Ē נכנסו×Ē + הפ×ĸל×Ē מ×Ēזמן פ×ĸיל + סמל ההגדרו×Ē ×Ēמיד מופי×ĸ בסו×Ŗ + 3 פ×ĸמים + × ×ĸילה ×ĸם Qrio + ׊×ĸ×Ē כיבוי + ק×Ļר + קלוריו×Ē + בלי נורי×Ē + הולך ונחלש + רטט חכם + Riiiver‎ + לאפשר ל־Wena לבקש מ־Gadgetbridge להוריד × ×Ēוני פ×ĸילו×Ē מהמכשיר מדי פ×ĸם בפ×ĸם + י×Ēרה ב־Suica + בינוני×Ē + הגדרו×Ē פ×ĸילו×Ē + ר×Ļי×Ŗ + מד ×Ļ×ĸדים + Qrio‎ + הש×ĸה כרג×ĸ + סנכרון × ×Ēונים בפ×ĸילו×Ē רק×ĸ + חלשה + סמלי מסך הבי×Ē + מזג אוויר בשור×Ē המ×Ļב + ×Ļלזיוס + הולך ומ×Ēגבר + בסיסי + כ×Ēב גדול יו×Ēר + אנרגיי×Ē גו×Ŗ + פ×ĸמיים + המכשיר ייכבה ויידלק אוטומטי×Ē לפי ×Ēזמון מוגדר + להפ×ĸיל א×Ē ה×Ē×Ļוגה ב×ĸ×Ē הס×Ēכלו×Ē ×ĸל פרק כ×Ŗ היד שלך + להגדיל א×Ē הכ×Ēב בלוח השנה, ה×Ēראו×Ē ו×ĸוד. + לחי×Ļה כפולה + מהיר + שחרור ×ĸם Qrio + סולם טמפרטורה + פ×ĸם אח×Ē + קבל×Ē ה×Ēראו×Ē ×ĸל שיחו×Ē + ה×Ļג×Ē סמל ה×Ēנאים הנוכחיים בפינה השמאלי×Ē ה×ĸליונה של מסך הבי×Ē + Wena Pay‎ + פ×ĸול×Ē כפ×Ēור + ׊×ĸ×Ē ה×Ēאוששו×Ē + רטט בשיחה נכנס×Ē + ×Ļב×ĸ נורי×Ē שיחה נכנס×Ē + ×Ļפירה + נא לבחור האם המכשיר יש×Ēמ׊ בסולם ×Ļלזיוס או פרנהייט. + סמלים ב×Ēפריט + חזרה ×ĸל רטט בשיחה נכנס×Ē + רטט ה×Ēראה + ללא הפסקה + 4 פ×ĸמים + פרנהייט + ×ĸו×Ļמ×Ē רטט + אם האפשרו×Ē כבויה, לא ×Ēופ×ĸנה ה×Ēראו×Ē ×ĸל שיחו×Ē נכנסו×Ē ב־Wena + לחי×Ļה ארוכה + ×Ēשלום + סדר ×ĸמוד מ×Ļב + הגדרו×Ē ׊×ĸון מ×ĸורר + חזרה ×ĸל רטט ה×Ēראה + הוספ×Ē ריבו×ĸים מ×ĸוגלים מסביב לסמלי מסך הבי×Ē + ×Ļב×ĸ נורי×Ē ה×Ēראה + כיבוי אוטומטי + י×Ēרה ב־Edy + אזהרה + טווח ׊×ĸון מ×ĸורר חכם + להש×Ēמ׊ ב×ĸי×Ļוב ×ĸשיר + היום מ×Ēחיל ב־ + הגדרו×Ē ה×Ēראה לפי יישומון + בלי רטט + חזקה + משולש + מסך פ×ĸילו×Ē + הגדרו×Ē ה×Ēראה + Sony WF-1000XM5‎ + הס×Ēר×Ē הגו×Ŗ בלבד + לטבי×Ē + סימנים נפו×Ļים + ה×Ē×ĸלמו×Ē מה×Ēראו×Ē פרופיל ×ĸבודה + לא לשלוח ה×Ēראו×Ē מיישומונים בפרופיל ה×ĸבודה של הש×ĸון \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index d32cf7b63..7ec96a4ba 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -8,14 +8,14 @@ SzinkronizÃĄlÃĄs Elveszett eszkÃļz keresÊse KÊpernyőkÊp kÊszítÊse - Kapcsolat megszÃŧntetÊse + LekapcsolÃŗdÃĄs EszkÃļz tÃļrlÊse %1$s tÃļrlÊse Az eszkÃļz Ês az Ãļsszes hozzÃĄtartozÃŗ adat tÃļrlÊsre kerÃŧl! NavigÃĄciÃŗs fiÃŗk megnyitÃĄsa NavigÃĄciÃŗs fiÃŗk bezÃĄrÃĄsa - Nyomja hosszan a szÊtkapcsolÃĄshoz - SzÊtkapcsolÃŗdÃĄs + Nyomja hosszan a lekapcsolÃŗdÃĄshoz + LekapcsolÃŗdÃĄs KapcsolÃŗdÃĄsâ€Ļ KÊpernyőkÊp kÊszítÊse az eszkÃļzről HibakeresÊs @@ -51,12 +51,12 @@ ÁltalÃĄnos beÃĄllítÃĄsok KapcsolÃŗdjon a Gadgetbridge eszkÃļzhÃļz a Bluetooth bekapcsolÃĄsakor Automatikus indulÃĄs - Automatikus ÃējracsatlakozÃĄs + Automatikus ÃējrakapcsolÃŗdÃĄs PreferÃĄlt zenelejÃĄtszÃŗ AlapÊrtelmezett DÃĄtum Ês idő Idő szinkronizÃĄlÃĄsa - Idő szinkronizÃĄlÃĄsa a Gadgetbridge eszkÃļzzel csatlakozÃĄskor, Ês amikor az Android eszkÃļz ideje vagy időzÃŗnÃĄja vÃĄltozik + Idő szinkronizÃĄlÃĄsa a Gadgetbridge eszkÃļzzel kapcsolÃŗdÃĄskor, Ês amikor az Android eszkÃļz ideje vagy időzÃŗnÃĄja vÃĄltozik TÊma VilÃĄgos SÃļtÊt @@ -113,12 +113,12 @@ NormÃĄl ÊrtesítÊsek Az ÊrtesítÊsszÃļveg eltolÃĄsa kÊpernyőn Csak az ÊrtesítÊsi ikon megjelenítÊse - Helyzet - PoziciÃŗ meghatÃĄrozÃĄsa + Hely + Hely meghatÃĄrozÃĄsa SzÊlessÊg HosszÃēsÃĄg - Helyzet frissen tartÃĄsa - AktuÃĄlis poziciÃŗ meghatÃĄrozÃĄsÃĄnak megprÃŗbÃĄlÃĄsa futÃĄsidőben, hiba esetÊn az eltÃĄrolt pozíciÃŗ hasznÃĄlata + Hely frissen tartÃĄsa + AktuÃĄlis hely meghatÃĄrozÃĄsÃĄnak megprÃŗbÃĄlÃĄsa futÃĄsidőben, hiba esetÊn az eltÃĄrolt hely hasznÃĄlata KÊrem, engedÊlyezze a hÃĄlÃŗzati helymeghatÃĄrozÃĄst Hely meghatÃĄrozva ÉrtesítÊsi protokoll erőltetÊse @@ -130,18 +130,18 @@ Pebble 2/LE GATT MTU hatÃĄrÊrtÊk Ha a Pebble 2/Pebble LE nem mÅąkÃļdik elvÃĄrtan, prÃŗbÃĄlja beÃĄllítani az MTU hatÃĄrÊrtÊket (az ÊrvÊnyes tartomÃĄny: 20–512) ÓraalkalmazÃĄs-naplÃŗzÃĄs engedÊlyezÊse - A karÃŗra-alkalmazÃĄsok naplÃŗit a Gadgetbridge is naplÃŗzni fogja (ÃējracsatlakozÃĄst igÊnyel) + A karÃŗra-alkalmazÃĄsok naplÃŗit a Gadgetbridge is naplÃŗzni fogja (ÃējrakapcsolÃŗdÃĄst igÊnyel) Korai ACK PebbleKit Minden esetben azonnal felismeri az Ãŧzeneteket, amelyeket kÃŧlső harmadik alkalmazÃĄsoknak kÃŧldenek - ÚjracsatlakozÃĄsi kísÊrletek + ÚjrakapcsolÃŗdÃĄsi kísÊrletek EgysÊgek IdőformÃĄtum Kijelző bekapcsolva tartÃĄsÃĄnak ideje EgÊsz napos pulzusmÊrÊs HPlus/Makibes beÃĄllítÃĄsok - Nincs csatlakozva - CsatlakozÃĄs - Csatlakozva + Nincs kapcsolÃŗdva + KapcsolÃŗdÃĄs + KapcsolÃŗdva Ismeretlen ÃĄllapot (ismeretlen) Teszt @@ -149,11 +149,11 @@ Ez egy teszt ÊrtesítÊs a Gadgetbridge-től A Bluetooth nem tÃĄmogatott. A Bluetooth ki van kapcsolva. - Koppintson az eszkÃļzre az alkalmazÃĄskezelőhÃļz - Koppintson az eszkÃļzre az aktivitÃĄshoz - Koppintson az eszkÃļzre a rezgetÊshez - Koppintson az eszkÃļzre a csatlakozÃĄshoz - Nem lehet csatlakozni. Lehet, hogy ÊrvÊnytelen a Bluetooth cím\? + Koppintson a kapcsolÃŗdott eszkÃļzre az alkalmazÃĄskezelőhÃļz + Koppintson a kapcsolÃŗdott eszkÃļzre az aktivitÃĄshoz + Koppintson a kapcsolÃŗdott eszkÃļzre a rezgetÊshez + Koppintson az eszkÃļzre a kapcsolÃŗdÃĄshoz + Nem lehet kapcsolÃŗdni. Lehet, hogy ÊrvÊnytelen a Bluetooth cím? A Gadgetbridge fut %1$d/%2$d binÃĄris telepítÊse Sikertelen telepítÊs @@ -170,10 +170,10 @@ N/A inicializÃĄlt %1$s , tőle: %2$s - EszkÃļz keresÊse + EszkÃļz felderítÊse KeresÊs leÃĄllítÃĄsa - KeresÊs indítÃĄsa - Új eszkÃļz csatlakoztatÃĄsa + FelderítÊs indítÃĄsa + Új eszkÃļz kapcsolÃŗdÃĄsa %1$s (%2$s) EszkÃļz pÃĄrosítÃĄsa HasznÃĄlja az Android Bluetooth pÃĄrosítÃĄs ablakot eszkÃļz pÃĄrosítÃĄsÃĄhoz. @@ -182,7 +182,7 @@ ÖsszekÃļttetÊs lÊtesítÊse ezzel: %1$s (%2$s) Sikertelen pÃĄrosítÃĄs ezzel: %1$s (%2$s) ÖsszekÃļttetÊs folyamatban ezzel: %1$s (%2$s) - %1$s (%2$s) mÃĄr pÃĄrosítva van, kapcsolÃŗdÃĄsâ€Ļ + %1$s (%2$s) mÃĄr Ãļssze van kÃļttetve, kapcsolÃŗdÃĄsâ€Ļ Nincs talÃĄlat a MAC címre, nem lehet pÃĄrosítani. EszkÃļzspecifikus beÃĄllítÃĄsok MI Band / Amazfit beÃĄllítÃĄsok @@ -194,7 +194,7 @@ ÉrvÊnytelen felhasznÃĄlÃŗi adatok vannak megadva, alapÊrtelmezett adatok hasznÃĄlata. Amikor rezegni kezd a Mi Band-je, Êrintse meg pÃĄrszor egymÃĄs utÃĄn. TelepítÊs - Tegye lÃĄthatÃŗvÃĄ a kÊszÃŧlÊkÊt. A jelenleg csatlakoztatott kÊszÃŧlÊkek nem fognak lÃĄthatÃŗkÊnt megjelenni. AktivÃĄlja a helymeghatÃĄrozÃĄst (Pl: GPS) az Android 6+ kÊszÃŧlÊkÊn. Kapcsolja ki a Privacy Guard-ban a Gadgetbridge-et (Ha van Privacy Guard beÃĄllítÃĄsa), kÃŧlÃļnben az alkalmazÃĄs Ãļsszeomolhat, illetve Ãējraindulhata kÊszÃŧlÊke. Ha nem talÃĄlhatÃŗ az eszkÃļz pÃĄr percen belÃŧl, prÃŗbÃĄlja meg Ãējraindítani a telefonjÃĄt. + Tegye felderíthetővÊ a kÊszÃŧlÊkÊt. A jelenleg kapcsolÃŗdott kÊszÃŧlÊkek nem fognak felderíthetőkÊnt megjelenni. AktivÃĄlja a helymeghatÃĄrozÃĄst (Pl: GPS) az Android 6+ kÊszÃŧlÊkÊn. Kapcsolja ki a Privacy Guard-ban a Gadgetbridge-et (Ha van Privacy Guard beÃĄllítÃĄsa), kÃŧlÃļnben az alkalmazÃĄs Ãļsszeomolhat, illetve Ãējraindulhat a telefonja. Ha nem talÃĄlhatÃŗ az eszkÃļz pÃĄr percen belÃŧl, prÃŗbÃĄlja meg Ãējraindítani a telefonjÃĄt. MegjegyzÊs: EszkÃļzkÊp NÊv/ÃĄlnÊv @@ -266,8 +266,8 @@ %1$s: %2$s %3$s Kompatibilis verziÃŗ Nem tesztelt verziÃŗ! - CsatlakozÃĄs az eszkÃļzhÃļz: %1$s - Pebble Firmware %1$s + KapcsolÃŗdÃĄs az eszkÃļzhÃļz: %1$s + Pebble firmware %1$s Helyes hardververziÃŗ HardververziÃŗ eltÊrÊs! %1$s (%2$s) @@ -298,7 +298,7 @@ KÃļnnyÅą alvÃĄs MÊly alvÃĄs Nem viselt - Nincs csatlakoztatva. + Nincs kapcsolÃŗdva. Mindegyik ÊbresztÊs letiltva AktivitÃĄsadatok eszkÃļzÃļn hagyÃĄsa Nem kompatibilis firmware @@ -324,7 +324,7 @@ Kezdete VÊge AdatÃĄtvitelre kÊszÃŧl: %1$s Ãŗta - VÃĄrakozÃĄs az ÃējracsatlakozÃĄsra + VÃĄrakozÃĄs az ÃējrakapcsolÃŗdÃĄsra Önről SzÃŧletÊsi Êv Nem @@ -345,10 +345,17 @@ Pulzus Pulzus Nyers adatok tÃĄrolÃĄsa az adatbÃĄzisban - Ha bejelÃļlÃļd, az adatok eredeti formÃĄban lesznek tÃĄrolva kÊsőbbi ÊrtelmezÊshez. Megj.: az adatbÃĄzis ebben az esetben nagyobb lesz! + Az adatokat eredeti formÃĄban tÃĄrolja, nÃļvelve az adatbÃĄzishasznÃĄlatot a kÊsőbbi feldolgozÃĄs lehetővÊ tÊtele ÊrdekÊben. AdatkezelÊs AdatkezelÊs - Az adatbÃĄzismÅąvelet ezt a helyet fogja hasznÃĄlni. Ez a hely elÊrhető mÃĄsik Android-alkalmazÃĄsok Ês a szÃĄmítÃŗgÊp szÃĄmÃĄra. Itt keresd az exportÃĄlt adatbÃĄzist is, illetve ide rakd az importÃĄlni kívÃĄnt adatbÃĄzist is. + Az exportÃĄlÃĄs/importÃĄlÃĄs mÅąveletek a kÃļvetkező kÃļnyvtÃĄr ÃētvonalÃĄt (lÃĄsd lentebb) hasznÃĄljÃĄk az eszkÃļzÊn. Ez a kÃļnyvtÃĄr elÊrhető mÃĄs Android alkalmazÃĄsok Ês a szÃĄmítÃŗgÊpe szÃĄmÃĄra. Jegyezze meg, hogy ez a kÃļnyvÃĄr Ês az Ãļsszes tartalmazott fÃĄjl tÃļrÃļlve lesz, ha eltÃĄvolítja a Gadgetbridge-et. Az adatok tartalmazzÃĄk: +\n Export_preference - globÃĄlis beÃĄllítÃĄsok +\n Export_preference_MAC - eszkÃļzspecifikus beÃĄllítÃĄsok +\n Gadgetbridge - eszkÃļz- Ês aktivitÃĄs-adatbÃĄzis +\n Gadgetbridge_date - egy dÃĄtumon exportÃĄlt adatbÃĄzis +\n *.gpx - GPS feljegyzÊsek +\n *.log - naplÃŗfÃĄjlok +\nVÃĄrhatÃŗan itt megtalÃĄlja az exportÃĄlt fÃĄjlokat (vagy helyezze ide az importÃĄlni kívÃĄnt fÃĄjlokat): RÊgi adatbÃĄzis tÃļrlÊse Nem elÊrhető az exportÃĄlÃĄsi Ãētvonal. KÊrjÃŧk vegye fel a kapcsolatot a fejlesztőkkel. ExportÃĄlva ide: %1$s @@ -377,7 +384,7 @@ Egy pÃĄrosítÃĄsi pÃĄrbeszÊdablak fog megjelennie az Android eszkÃļzÊn. Ha ez nem tÃļrtÊnik meg, nÊzze meg az ÊrtesítÊsi sÃĄvban Ês fogadja el a pÃĄrosítÃĄsi kÊrelmet. UtÃĄna fogadja el a kÊrelmet a Pebble-n is. Bizonyosodjon meg rÃŗla, hogy a skin tÃĄmogatja-e az IdőjÃĄrÃĄs ÊrtesítÊsek megjelenítÊsÊt. Nincs szÃŧksÊg konfigurÃĄciÃŗra. A Pebble app management-ben tudja engedÊlyezni a rendszer időjÃĄrÃĄs alkalmazÃĄsÃĄt. Azok a skinek amik tÃĄmogatjÃĄk az időjÃĄrÃĄs ÊrtesítÊsket, automatikusan megfogjÃĄk jeleníteni azt. Bluetooth pÃĄrosítÃĄs engedÊlyezÊse - Kapcsolja ki ezt, ha gondja van a csatlakozÃĄssal + Kapcsolja ki ezt, ha gondja van a kapcsolÃŗdÃĄssal Metrikus Birodalmi 24H @@ -394,16 +401,16 @@ Automatikus (alvÃĄsÊrzÊkelÊs) Időzített (idő intervallum) PÃĄrosítÃĄs megkísÊrlÊse ezzel: %1$s - A csatlakozÃĄs a(z) %1$s eszkÃļzzel azonnal megszakadt. - CsatlakozÃĄs megkísÊrlÊse ezzel: %1$s + Az ÃļsszekÃļttetÊs a(z) %1$s eszkÃļzzel azonnal megszakadt. + KapcsolÃŗdÃĄs megkísÊrlÊse ezzel: %1$s EngedÊlyezze a Bluetooth-ot az eszkÃļzÃļk felderítÊsÊhez. - PÃĄrosítva ehhez: %1$s. - %1$s pÃĄrosítÃĄsa\? + ÖsszekÃļttetve ezzel: %1$s. + %1$s pÃĄrosítÃĄsa? VÃĄlassza ki a pÃĄrosítÃĄst az eszkÃļz pÃĄrosítÃĄsÃĄhoz. Ha ez nem sikerÃŧl, prÃŗbÃĄlja meg Ãējra pÃĄrosítÃĄs nÊlkÃŧl. PÃĄrosítÃĄs Ne pÃĄrosítsa AdomÃĄnyozÃĄs - CsatlakoztatÃĄsâ€Ļ + KapcsolÃŗdÃĄsâ€Ļ IdőjÃĄrÃĄs AktivitÃĄsadatok automatikus lekÊrdezÊse Vízszintes @@ -455,9 +462,9 @@ %1$ akkumulÃĄtora merÃŧl: %2$s A gyÃĄri beÃĄllítÃĄsok visszaÃĄllítÃĄsa minden adatot tÃļrÃļl a kapcsolÃŗdott eszkÃļzről (ha tÃĄmogatott). A Xiaomi/Huami eszkÃļzÃļknek emellett megvÃĄltozik a Bluetooth MAC-címe is, így Ãēj eszkÃļzkÊnt jelennek meg a Gadgetbridge szÃĄmÃĄra. nodomain.freeyourgadget.gadgetbridge.ButtonPressed - EgÊsz napos szívritmus mÊrÊs + EgÊsz napos pulzusmÊrÊs KÊpernyő tÃĄjolÃĄsa - NaptÃĄr ÊrtesítÊsek bekapcsolÃĄsa mikor nincs kapcsolat a telefonnal + NaptÃĄr ÊrtesítÊsek bekapcsolÃĄsa, akkor is, ha le van kapcsolÃŗdva a telefonrÃŗl Ön a %s firmware-t telepíti az Amazfit Cor-ra. \n \nGyőződjÃļn meg, hogy a .fw, Ês vÊgÃŧl a .res fÃĄjl is telepítÊsre kerÃŧl! Az Ãŗra a .fw file telepítÊse utÃĄn Ãējraindul. @@ -568,7 +575,7 @@ JelzÊs típusÃĄnak beÃĄllítÃĄsa az ÊbresztőhÃļz Ébresztő beÃĄllítÃĄsa ennyi idő mÃēlva: ÉbresztÊs - Nincs csatlakozva, Êbresztő nincs beÃĄllítva. + Nincs kapcsolÃŗdva, Êbresztő nincs beÃĄllítva. Kezek Ês lÊpÊsek Megvan! AdatbÃĄzis ÃŧrítÊse @@ -633,7 +640,7 @@ Biztosan tÃļrÃļlni szeretne %d aktivitÃĄst\? 7 nap SportaktivitÃĄsok - VoIP alkalmazÃĄshívÃĄsok engedÊlyezÊse + VoIP alkalmazÃĄs hívÃĄsainak engedÊlyezÊse NaptÃĄrak feketelistÃĄzÃĄsa ExportÃĄlÃĄs helye Automatikus exportÃĄlÃĄs engedÊlyezve @@ -645,7 +652,7 @@ Gyors ÊbresztÊs 1 Ãŗra Napi cÊl: zsírÊgetÊs ideje percben - Gadgetbridge Nightly + Gadgetbridge nightly 5 perc Automatikus %1$s, minden nap @@ -658,7 +665,7 @@ KÊpernyő bekapcsolva tartÃĄsa edzÊs kÃļzben Tegnap Mi Band 3 - Gadgetbridge Nightly Pebble nÊlkÃŧl + Gadgetbridge nightly Pebble nÊlkÃŧl GPS kÃŧldÊse edzÊs kÃļzben Összes edzÊs 6.3k @@ -666,7 +673,7 @@ ÉbresztÊs EdzÊs A Gadgetbridge frissítÊse utÃĄni vÃĄltozÃĄsnaplÃŗ megjelenítÊse az utolsÃŗ verziÃŗ Ãŗta - About Gadgetbridge Nightly Pebble nÊlkÃŧl nÊvjegye + Gadgetbridge nightly Pebble nÊlkÃŧl nÊvjegye Napi cÊl: ÃĄllÃĄsi idő percben Automatikus Watch X Plus kalibrÃĄciÃŗ @@ -683,13 +690,13 @@ %d Ãŗra %d Ãŗra - Gadgetbridge Nightly nÊvjegye + Gadgetbridge nightly nÊvjegye Egy karkÃļtő UtolsÃŗ edzÊs VÃĄltozÃĄsnaplÃŗ megjelenítÊse indulÃĄskor NYERS adatok megjelenítÊse az aktivitÃĄsgrafikonon ÚjdonsÃĄgok - Bangle.js Gadgetbridge (Nightly) nÊvjegye + Bangle.js Gadgetbridge (nightly) nÊvjegye 3 Ãŗra Minimum pulzus Tegnapi aktivitÃĄs @@ -702,7 +709,7 @@ Napi cÊl: tÃĄvolsÃĄg mÊterben Összesen %d lÊpÊs naponta KÊrdezzen előszÃļr - Bangle.js Gadgetbridge (Nightly) + Bangle.js Gadgetbridge (nightly) 1k ÉszlelÊs engedÊlyezÊse Amazfit Band 5 @@ -897,7 +904,7 @@ Jelenítse meg, amikor ezeket a szavakat tartalmazza Naplementekor Pulzusadatok lekÊrdezÊse - EszkÃļz csatlakoztatva + EszkÃļz kapcsolÃŗdva TradicionÃĄlis kínai MyKronoz ZeTime sec/m @@ -930,7 +937,7 @@ Minimum percenkÊnti lÊpÊs futÃĄs ÊszlelÊsÊhez InaktivitÃĄsi ÊrtesítÊs a vÃĄltoztatÃĄs eltarthat nÊhÃĄny mÃĄsodpercigâ€Ļ - Az Ãŗra nincs csatlakozva + Az Ãŗra nincs kapcsolÃŗdva Havi alvÃĄs CsÃļkkenő KÃļnnyÅą alvÃĄs @@ -946,4 +953,602 @@ Élő nÊzet Minden %d. percben lekÊrdezi Automatikus lekÊrdezÊs + Címke + Magas + Y5 + Idő + ManuÃĄlis + + Alacsony + BeÃĄllítÃĄs + (elavult) + PontossÃĄg + Magas prioritÃĄsÃē + EgyensÃēly + LitvÃĄn + BeszÊd + JelszÃŗ + KezdÊs + Offline hang + AlkalmazÃĄsok + kÃļrÃļk + Nyugodt + NaptÃĄr + perc/km + OptimalizÃĄlÃĄs + ###.# km + Tiszta + AnalizÃĄlÃĄsâ€Ļ + NarancssÃĄrga + BengÃĄli + Nem + NormÃĄl + Bal + Rendszer + Alacsony + Ismeretlen + Izgatott + Bangle.js + Spotify + iTag + MÃŗd + Jobb + KiegyrnsÃēlyozott + Átlagos + Összes mÅąhold + Dinamikus színek + Cseh + Igen + TLW64 + FigyelmeztetÊs + PontossÃĄg előszÃļr + BefejezÊs + ZseblÃĄmpa + Jobb + Ettől + Perzsa + Klub + EgyÊb + GPS + BefejezÊsâ€Ļ + KezdÊs + Minimum + Eddig + tÃļrlÊs + ÉrzÊkenysÊg + KezdÊsâ€Ļ + KosÃĄrlabda + ÉrzÊkeny + ArÊna + NEM TÁMOGATOTT + KezdÊs + Izlandi + Bal + TÃērÃĄzÃĄs + ElÃŧlső + EgÊszsÊg + ÓramutatÃŗ jÃĄrÃĄsÃĄval ellentÊtesen + LetÃļltÊs gyorsítÃŗtÃĄrba + Kapcsolat + IdőjÃĄrÃĄs + Ki + Hangok + SzÅąrő + TÃĄvolsÃĄg + AlkalmazÃĄsonkÊnri beÃĄllítÃĄsok + MÊrÊs + Ki + Stopper + EsemÊnyek + NormÃĄl + Futball + SkandinÃĄv + Wasp-os + Alexa + Dinamikus + Észt + FÃĄjl kivÃĄlasztÃĄsa + Stopper + VÃļrÃļs + Ki + SzerkesztÊs + Ki + AktivÃĄlÃĄs + MenÃŧ + UkrÃĄn + BaromÊter + Email + HőmÊrsÊklet + KalibrÃĄlÃĄs + lÊpÊs + mÊgse + LÃĄgy + LÊpÊsek + ### m + ÓramutatÃŗ jÃĄrÃĄsÃĄnak megfelelően + Ki + DÃĄtum + Magas + ExponenciÃĄlis + FitPro + %s letÃļltve gyorsítÃŗtÃĄrba + Ki + Kijelző + Hiba az alkalmazÃĄs letÃļltÊsekor + AktivitÃĄsok + EgyÊni + Semmi + SebessÊg előszÃļr + UM-25 + BFH-16 + Bangle.js Gadgetbridge (nightly) + Gadgetbridge (nightly) + LÊpÊsek + MÃĄszÃĄs + TÃĄvolsÃĄg + Aktív lÊpÊsek + KÊrÊs feldolgozÃĄsaâ€Ļ + KÃļvetkező szÃĄm + MÊrÊs eredmÊnyek + ElvetÊs + Amazfit Pop + ExtrÊm + Összes aktivitÃĄs + Hitelesítő kulcs + Lemfo SG2 + AjÃĄnlÃĄs + NyÃējtÃĄs + RÃļvid (15s) + Cím + GPS + Bluetooth + Kapcsolatok + ZeTime beÃĄllítÃĄsok + Fallabda + Bose QC35 + \'%1$s\' tÃļrlÊse + Tenisz + SSID + SíelÊs + Casio GBX-100 + Új mÅąvelet + Stílus + EgyÊb + Kikapcsolva + ÉrtesítÊsek engedÊlyezÊse + AktivitÃĄs cÊl + HangfÃŗkusz + Aktív idő + Pulzus + VillogÃĄs + TovÃĄbb aludt: %1$s + 100 lÊpÊs + ExportÃĄlja az adatokat\? + Domyos T540 + SÃēlyemelÊs + Anaerobik + Összes + Amazfit GTS + SzÅąrő alkalmazÃĄsa + EszkÃļzinformÃĄciÃŗ + HitelesítÊs + RÃļgbi + LezÃĄrÃĄs + Mindig + Összes eszkÃļz + Amazfit Neo + Enyhe + FÊnyessÊg + LeírÃĄs + EgyÊni 2 + Fejlesztői beÃĄllítÃĄsok + Telefon keresÊse + FelhasznÃĄlÃŗnÊv + IdőzÃŗna + TÃĄnc + Bekapcsolva + TÃĄnc + KÃĄrtyÃĄk + AlkalmazÃĄslista + Box + JÃĄtÊk mÃŗd + SzÃļrf + AkkumulÃĄtorkímÊlő + EszkÃļz keresÊse + Fizikai gombok + AdatÃĄtvitel + EszkÃļz kivÃĄlasztÃĄsa + Telefon nÊmítÃĄsa + MozgÃĄsintenzitÃĄs + Sony WH-1000XM3 + TÃļbbet lÊpett: %1$d + Magas + Wifi + Hangerő nÃļvelÊse + MÅąvelet szerkesztÊse + MÊrsÊkelt + TÃļrlÊs kÊsz + MegÃĄllítÃĄs + EszkÃļzbeÃĄllítÃĄsok + NÊv + 10 lÊpÊs + AlsÃŗ + Dolby mÃŗd + Maximum pulzus + 80% + Gimnasztika + HangerőszabÃĄlyzÃĄs + Hangerő + Sony WF-1000XM3 + 85% + ElőrejelzÊs + NavigÃĄciÃŗ + Kickbox + CÊlok + VisszaszÃĄmlÃĄlÃĄs + VESC + Állapot + Stressz + Nyugodt + Átlagos sebessÊg + Címke szerkesztÊse + AkkumulÃĄtortok + Amazfit X + Cím + SMA-Q2 OSS + +%d + MÁR ÖSSZE VAN KÖTTETVE + Előző szÃĄm + ErősítÊs + KÃŗd + Okos + Sony WF-SP800N + GrÃēziai + AsteroidOS + 90% + KÃļvetkező + Tartalom megjelenítÊse + Zepp E + Hangerő csÃļkkentÊse + SzinkronizÃĄciÃŗ + NÊv szerkesztÊse + Leghosszabb + Golf + KÍSÉRLETI + Tiszta basszus + Ki + KÊzilabda + EgyÊni 1 + HívÃĄskezelÊs + TennivalÃŗ + LejÃĄtszÃĄsvezÊrlÊs + Előző + Telefon + Nut mini + NaptÃĄr + HorvÃĄt + HÃĄttÊrkÊp + + KonfigurÃĄciÃŗ mentÊse + Aktív percek + Üzenet + Pomodoro kÃļvető + Felső + Tartalom elrejtÊse + Minimalista + FÃĄjlkezelÊs + Minimum pulzus + BemelegítÃĄs + VÊrnyomÃĄs + AlkalmazÃĄs + AlkalmazÃĄskezelÊs + RÃļplabda + Sony WH-1000XM4 + Zumba + MozgÃĄs +\nIntenzitÃĄs + FÊnyek + AGPS + Aerobik + Galaxy Buds + RiasztÃĄs + 1 lÊpÊs + Az alkalmazÃĄs letÃļltÊse elindult + EnergiatakarÊkos + A Bangle.js fut + Ez a mÅąvelet az Ãļsszes kapcsolÃŗdott eszkÃļz beÃĄllítÃĄsÃĄt alap helyzetbe helyezi. Biztos benne? + GPS + GLONASS + GPS + BDS + Alacsony energiÃĄjÃē GPS + 110 bpm + 135 bpm + MÊrÊsek gyakorisÃĄga + 105 bpm + 40 bpm + Pulzus riasztÃĄs + 130 bpm + Pulzus beÃĄllítÃĄsok + Jelenlegi / maximum pulzus: %1$d / %2$d + PulzusmÊrÊs beÃĄllítÃĄsa + EgÊsz napos mÊrÊs + PulzusmÊrÊs + Pulzus riasztÃĄsok + 125 bpm + 100 bpm + 145 bpm + 115 bpm + 150 bpm + Automatikus pulzusmÊrÊsek + 140 bpm + AlvÃĄs kÃļzben vÊgezzen mÊrÊseket + 45 bpm + 120 bpm + Pulzus riasztÃĄs engedÊlyezÊse + 50 bpm + A helymeghatÃĄrozÃĄsnak engedÊlyezve kell lennie + HÃĄlÃŗzat hasznÃĄlata csak a hely meghatÃĄrozÃĄsÃĄhoz + GPS hely kÃŧldÊse %1$d eszkÃļzÃļkre + Tiszta fehÊr + Átlagos pulzus + MÃŗdosítsa a hitelesítÊsi kulcsot egy kÃļzÃļs kulcsra az Ãļsszes olyan Android-eszkÃļzÊn, amelyről kapcsolÃŗdni szeretne. A korÃĄbbi alapÊrtelmezett kulcs minden eszkÃļzÃļn: 0123456789@ABCDE + HÃĄromszor + SebessÊg vÃĄrosa + Telefon nÊma mÃŗd + MÃĄsodik időzÃŗna widget + IdőjÃĄrÃĄs-informÃĄciÃŗ gyorsítÃŗtÃĄrazÃĄsa + RÃļvid + Az eszkÃļz kapcsolÃŗdik + Pulzus szín + Bluetooth keresÊs: + Szabad kombinÃĄciÃŗ + Az automatikus exportÃĄlÃĄs engedÊlyezve van. + FÃĄjlok tÃļrlÊse az exportÃĄlÃĄs/importÃĄlÃĄs kÃļnyvtÃĄrbÃŗl + Okos rezgÊs + Üzleti stílus + Egy szÃĄmlap ezzel a nÊvvel mÃĄr lÊtezik a gyorsítÃŗtÃĄrban. SzeretnÊ felÃŧlírni? + KÃļzepes + Smaragd holdfÊny + ExportÃĄlÃĄs/importÃĄlÃĄs kÃļnyvtÃĄr tartalma + NormÃĄl / rezgÊs + LekapcsolÃŗdÃĄsi ÊrtesítÊs + Folyamatos + A kapcsolÃŗdÃĄs nincs megerősítve az ÃŗrÃĄn, hitelesítÊs nÊlkÃŧli mÃŗd hasznÃĄlata + ÉlÊnk + Az automatikus exportÃĄlÃĄs nincs időzítve. + Az exportÃĄlt fÃĄjlok az exportÃĄlÃĄs/importÃĄlÃĄs kÃļnyvtÃĄrban bÃĄrmilyen alkalmazÃĄs szÃĄmÃĄra elÊrhetőek az eszkÃļzÊn. Lehet, hogy tÃļrÃļlni szeretnÊ ezeket a fÃĄjlokat szinkronizÃĄlÃĄs vagy biztonsÃĄgi mentÊs utÃĄn. GyőződjÃļn meg, hogy van mentÊse, mielőtt tÃļrli őket. A GPX fÃĄjlok, az alkÃļnyvtÃĄrak Ês az automatikusan exportÃĄlt adatbÃĄzis-fÃĄjlok (ha lÊteznek) nem lesznek tÃļrÃļlve. Az exportÃĄlÃĄs/importÃĄlÃĄs kÃļnyvtÃĄr Ãētvonala: + Gyenge + Az automatikus exportÃĄlÃĄs nincs engedÊlyezve. + RezgÊsmintÃĄk beÃĄllítÃĄsa a kÃŧlÃļnbÃļző ÊrtesítÊsekhez + Csillagos Êg + A beírt titkos hitelesítÊsi kulcs ÊrvÊnytelen! Nyomjon hosszan az eszkÃļzre a szerkesztÊshez. + ExportÃĄlÃĄs/importÃĄlÃĄs kÃļnyvtÃĄr tartalmÃĄnak megjelenítÊse + RezgÊs hívÃĄsokhoz, Ãŧzenetekhez, ÊrtesítÊsekhez Ês tÃļbbhÃļz + VÃļrÃļs fantÃĄzia + Pulzus riasztÃĄs sport aktivitÃĄs kÃļzben + Az eszkÃļz lekapcsolÃŗdik! + Alap + TÃļrli a fÃĄjlokat az exportÃĄlÃĄs/importÃĄlÃĄs kÃļnyvtÃĄrbÃŗl? + KÊtszer + Ennek az eszkÃļznek szÃŧksÊge van egy titkos hitelesítÊsi kulcsra, nyomjon hosszan az eszkÃļzre, hogy be tudja írni. Olvassa ez a wikit. + Biztosan tÃļrli a fÃĄjlokat az exportÃĄlÃĄs/importÃĄlÃĄs kÃļnyvtÃĄrbÃŗl? + Gyors + Sok kÃļszÃļnet minden fel nem sorolt kÃļzremÅąkÃļdőnek, akik kÃļzremÅąkÃļdtek ezekben: kÃŗdok, fordítÃĄsok, tÃĄmogatÃĄs, Ãļtletek, motivÃĄciÃŗ, hibajelentÊsek, pÊnzâ€Ļ ✊ + RezgÊs / nÊma + Egyszer + LÃĄthatÃŗ, amíg kapcsolÃŗdva van + Pulzus zÃŗnÃĄk + MentÊs Ês alkalmazÃĄs + BejÃļvő hívÃĄs rezgÊs + Az időjÃĄrÃĄs-informÃĄciÃŗ gyorsítÃŗtÃĄrazva lesz az alkalmazÃĄs-ÃējraindítÃĄsok kÃļzÃļtt. + Hatalmas Êg + A gyÃĄrtÃŗk zÃĄrt forrÃĄskÃŗdÃē Android-kÃŧtyÃŧalkalmazÃĄsainak felhő nÊlkÃŧli, szabad, copyleft helyettesítÊse. A Gadgetbridge nightly kiadÃĄsa. Ebben a verziÃŗban a Pebble szolgÃĄltatÃŗt ÃĄtneveztÊk az ÃŧtkÃļzÊsek elkerÃŧlÊse ÊrdekÊben, így egyes Pebble-lel kapcsolatos integrÃĄciÃŗk nem mÅąkÃļdnek, de telepíthető a meglÊvő Gadgetbridge-telepítÊs mellÊ. + UtolsÃŗ automatikus exportÃĄlÃĄs: %1$s + EgyszerÅą adatok + Új hitelesítÊsi protokoll + SzÃĄmlap konfigurÃĄciÃŗ + GyorsítÃŗtÃĄrazÃĄs, amíg hatÃŗtÃĄvon kívÃŧl van + Bluetooth LE keresÊs: + Az eszkÃļz lekapcsolÃŗdott! + Legyen pulzusmÊrÊs + VillÃĄmlÃĄs + SzirÊna + Ennek az opciÃŗnak az engedÊlyezÊse ignorÃĄlni fogja keresÊskor azokat az eszkÃļzÃļket, amik mÃĄr ÃļsszekÃļttetve/pÃĄrosítva vannak + Automatikus exportÃĄlÃĄs futtatÃĄsa most + BejÃļvő hívÃĄs rezgÊs ismÊtlÊs + ÉrtesítÊs rezgÊs + Folyamatosan + FelderítÊsi Ês pÃĄrosítÃĄsi beÃĄllítÃĄsok + NÊgyszer + Kihagyott ÊrtesítÊsek elkÃŧldÊse, amikor az eszkÃļz ÃējrakapcsolÃŗdik, miutÃĄn hatÃŗtÃĄvon kívÃŧl volt + RezgÊs erőssÊge + Az Ãŗra figyelmeztetni fog, amikor a pulzus ÃĄtlÊpi a hatÃĄrokat. + A karkÃļtő rezegni fog, ha a telefon lekapcsolÃŗdik a karkÃļtőről + Sok adat + A gyÃĄrtÃŗk zÃĄrt forrÃĄskÃŗdÃē Android-kÃŧtyÃŧalkalmazÃĄsainak felhő nÊlkÃŧli, szabad, copyleft helyettesítÊse. A Gadgetbridge nightly kiadÃĄsa. Nem telepíthető, ha mÃĄr telepítve van a Gadgetbridge vagy a Pebble alkalmazÃĄs, a Pebble szolgÃĄltatÃŗ ÃŧtkÃļzÊse miatt. + ÖsszekÃļttetett eszkÃļzÃļk ignorÃĄlÃĄsa + ÉrtesítÊs rezgÊs ismÊtlÊs + ForgÃŗ fÃļld + Kommit %s + EngedÊlyezze ezt az opciÃŗt, ha az eszkÃļze nem talÃĄlhatÃŗ felderítÊs sorÃĄn + Nem elÊrhető hitelesítÊs nÊlkÃŧli mÃŗdban + NormÃĄl / nÊma + FigyelmeztetÊs + A hitelesítÊs sikertelen, korlÃĄtozott funkcionalitÃĄs + Ha \"Frissítse az alkalmazÃĄst a legutÃŗbbi verziÃŗra\" Ãŧzenetet kap, győződjÃļn meg rÃŗla, hogy engedÊlyezte az \"Új hitelesítÊsi protokoll\" opciÃŗt fentebb. Koppintson ide a tovÃĄbbi rÊszletekÊrt a wikiben. + Nincs rezgÊs + Erős + HÃĄromszor + NÊhÃĄny eszkÃļznek szÃŧksÊge van egy kÃŧlÃļnleges pÃĄrosítÃĄsi kulcsra az első alkalommal. Koppintson ide a tovÃĄbbi rÊszletekÊrt a wikiben. + Android tÃĄrsalkalmazÃĄs a Bangle.js szÃĄmÃĄra, amely a Gadgetbridge projektre ÊpÃŧl, hozzÃĄadott internet-hozzÃĄfÊrÊssel. +\n +\nA Google Play ÁruhÃĄz szabÃĄlyzata miatt nem lehet az adomÃĄnyozÃĄsi link magÃĄban az alkalmazÃĄsban, de ha tetszik az alkalmazÃĄs, kÊrjÃŧk, fontolja meg az adomÃĄnyozÃĄst az alÃĄbbi Gadgetbridge honlapon keresztÃŧl. + Gyors elÊrÊs (hÃĄrom ÊrintÊs) + Sony WH-1000XM2 + A nightly GB fut + Gadgetbridge (nightly, Pebble szolgÃĄltatÃŗ nÊlkÃŧl) + a nightly Pebble nÊlkÃŧli GB fut + BeltÊri kerÊkpÃĄrozÃĄs + Sony WH-1000XM5 + A nightly Bangle.js fut + BeltÊri fitnesz + Nem fut + sÊta felismerÊse + evezÊs felismerÊse + BeltÊri jÊgkorcsolyÃĄzÃĄs + JÊgkorcsolyÃĄzÃĄs + Sony LinkBuds S + BeltÊri sÊta + Gyors elÊrÊs (kettő ÊrintÊs) + Sony WF-1000XM4 + Sony WF-1000XM5 + FutÃĄs + kerÊkpÃĄrozÃĄs felismerÊse + Pebble (cipőcsat) + Ez csak a Pebble 2 szÃĄmÃĄra kÊszÃŧlt Ês kísÊrleti, prÃŗbÃĄlja ezt, ha kapcsolÃŗdÃĄsi problÊmÃĄi vannak. + futÃĄs felismerÊse + Android tÃĄrsalkalmazÃĄs a Bangle.js szÃĄmÃĄra, amely a Gadgetbridge projektre ÊpÃŧl, hozzÃĄadott internet-hozzÃĄfÊrÊssel. +\n +\nA Google Play ÁruhÃĄz szabÃĄlyzata miatt nem lehet az adomÃĄnyozÃĄsi link magÃĄban az alkalmazÃĄsban, de ha tetszik az alkalmazÃĄs, kÊrjÃŧk, fontolja meg az adomÃĄnyozÃĄst az alÃĄbbi Gadgetbridge honlapon keresztÃŧl. + KÃŧltÊri futÃĄs + MegnyitÃĄs Android eszkÃļzÃļn + KÃŧltÊri kerÊkpÃĄrozÃĄs + HÃĄttÊrbeli JS engedÊlyezÊse + Sony Wena 3 + KÃŧltÊri színpad + Az adatbÃĄzis exportÃĄlÃĄsa meghiÃēsult. Ellenőrizze a beÃĄllítÃĄsait! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit Cor 2 kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy a .fw fÃĄjlt, majd ezt kÃļvetően a .res fÃĄjlt is telepíti. A karkÃļtő a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res fÃĄjlt, ha az pontosan megegyezik a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! +\n +\nTELJESEN TESZTELETLEN, VALÓSZÍNÅ°LEG A BEATS_W FIRMWARE-t kell feltÃļltenie, HA A KÉSZÜLÉK NEVE \"Amazfit Band 2\" + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTS kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy telepíti a .fw fÃĄjlt, a .res fÃĄjlt, vÊgÃŧl pedig a .gps fÃĄjlt. Az Ãŗra a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit Verge Lite kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy telepíti a .fw fÃĄjlt, a .res fÃĄjlt, vÊgÃŧl pedig a .gps fÃĄjlt. Az Ãŗra a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTS 4 kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Mi Band 5 kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy előszÃļr a .fw fÃĄjlt telepítse, utÃĄna pedig a .res fÃĄjlt. A karkÃļtő a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Mi Band 6 kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy előszÃļr a .fw fÃĄjlt telepítse, utÃĄna pedig a .res fÃĄjlt. A karkÃļtő a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTS 3 kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTR 3 PRO kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTR 4 kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit T-Rex 2 kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit Bip 3 Pro kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy a .fw fÃĄjlt, majd ezt kÃļvetően a .res fÃĄjlt is telepíti. Az ÃŗrÃĄja az .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res fÃĄjlt, ha az pontosan megegyezik a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit X kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk győződjÃļn meg rÃŗla, hogy előszÃļr telepítse a .fw fÃĄjlt, majd ezt kÃļvetően a .res fÃĄjlt. A .fw fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res fÃĄjlt, ha az pontosan ugyanaz, mint a korÃĄbban telepített. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + GPS + GALILEO + Ön telepíteni kívÃĄnja a %s firmware-t az Mi Band 3 kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy előszÃļr a .fw fÃĄjlt telepítse, utÃĄna pedig a .res fÃĄjlt. A karkÃļtő a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTR kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy telepíti a .fw fÃĄjlt, a .res fÃĄjlt, vÊgÃŧl pedig a .gps fÃĄjlt. Az Ãŗra a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit T-Rex kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy telepíti a .fw fÃĄjlt, a .res fÃĄjlt, vÊgÃŧl pedig a .gps fÃĄjlt. Az Ãŗra a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Mi Band 4 kÊszÃŧlÊkÊre. +\n +\nKÊrjÃŧk, győződjÃļn meg rÃŗla, hogy előszÃļr a .fw fÃĄjlt telepítse, utÃĄna pedig a .res fÃĄjlt. A karkÃļtő a .fw fÃĄjl telepítÊse utÃĄn Ãējraindul. +\n +\nMegjegyzÊs: Nem kell telepítenie a .res Ês .gps fÃĄjlokat, ha azok pontosan megegyeznek a korÃĄbban telepítettel. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t a Xiaomi Smart Band 7 kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTS 4 Mini kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit GTR 3 kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit Band 7 kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + Ön telepíteni kívÃĄnja a %s firmware-t az Amazfit Cheetah Pro kÊszÃŧlÊkÊre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn a karkÃļtője Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! + A %s firmware-t kÊszÃŧl telepíteni a %s kÊszÃŧlÊkre. +\n +\nA .zip fÃĄjl telepítÊse utÃĄn az ÃŗrÃĄja Ãējraindul. +\n +\nFOLYTATÁS CSAK SAJÁT FELELŐSSÉGRE! \ No newline at end of file diff --git a/app/src/main/res/values-ia/strings.xml b/app/src/main/res/values-ia/strings.xml new file mode 100644 index 000000000..a6b3daec9 --- /dev/null +++ b/app/src/main/res/values-ia/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index e1234caef..3e39e22a2 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -1178,7 +1178,7 @@ Sembunyikan nama tapi tampilkan nomor Sembunyikan nomor tapi tampilkan nama Sembunyikan nama dan nomor - Sembunyikan konten + Sembunyikan semua konten Lokasi cuaca (untuk penyedia cuaca LineageOS) Kalender yang di daftar hitam tidak akan disinkronkan ke perangkat Pesan kalengan @@ -1515,7 +1515,7 @@ Aktifkan ini jika perangkat Anda tidak memiliki dukungan untuk fon bahasa Anda Memanjangkan atau memendekkan baris teks Kanan ke Kiri yang dipisah Mode privasi pesan - Tampilkan konten + Tampilkan semua konten Daftar aplikasi Perbolehkan aplikasi lain untuk mengakses data detak jantung saat Gadgetbridge sedang terhubung Terlihat saat sedang terhubung @@ -2387,4 +2387,101 @@ Prancis (Kanada) Prancis (Prancis) Perangkat tidak memiliki ruang penyimpanan kosong yang cukup + MTU %1$d saat ini terlalu rendah, silakan aktitkan MTU yang tintgi dalan pengaturan perangkat dan putuskan/hubungkan ulang perangkat. + Kirim teks ke perangkat + Mi Band HRX + Aktifkan aktivitas penemuan baru, yang seharusnya memperbaiki masalah penemuan perangkat. Nonaktifkan ini jika Anda menghadapi masalah apa pun saat mencari atau memasangkan perangkat Anda. + Aktifkan aktivitas penemuan baru + Kiri + Pengaturan Aktivitas + Putih + Kanan + Pengaturan Tampilan + Hijau + Kuning + Sony Wena 3 + Merah + Sian + Tengah + Ungu + Biru + Pengaturan Panggilan Masuk + Mulai pewaktu aktif + Ikon Pengaturan selalu ditampilkan pada akhir + Tiga kali + Kunci Qrio + Waktu daya mati + Pendek + Kalori + Tanpa LED + Melangkah Bawah + Getaran Pintar + Riiiver + Perbolehkan Wena untuk meminta Gadgetbridge mengunduh data aktivitas dari perangkat + Saldo Suica + Sedang + Berkelanjutan + Hitungan Langkah + Qrio + Waktu Saat Ini + Sinkronisasi Data Aktivitas Latar Belakang + Lemah + Ikon Layar Beranda + Cuaca Dalam Bilah Status + Celsius + Melangkah Atas + Dasaran + Ukuran Fon Lebih Besar + Energi Badan + Dua kali + Perangkat akan dimatikan dan dinyalakan secara otomatis berdasarkan jadwal yang ditentukan + Nyalakan layar ketika Anda melihat pergelangan tangan Anda + Perbesar ukuran fon dalam kalender, notifikasi, dll. + Tekan Ganda + Cepat + Buka Kunci Qrio + Skala suhu + Sekali + Terima Pemberitahuan Panggilan + Tampilkan ikon kondisi saat ini di sudut kiri atas layar beranda + Wena Pay + Tindakan Tombol + Waktu daya nyala + Getaran Panggilan Masuk + Warna LED Panggilan Masuk + Sirene + Pilih apakah perangkat menggunakan skala Celsius atau Fahrenheit. + Ikon Menu + Pengulangan Getaran Panggilan Masuk + Getaran Pemberitahuan + Tidak terbatas + Empat kali + Fahrenheit + Kekuatan Getaran + Jika dimatikan, Anda tidak akan diberi tahu tentang panggilan masuk di Wena + Tekan Lama + Pembayaran + Pengurutan Halaman Status + Pengaturan Alarm + Pengulangan Getaran Pemberitahuan + Menambahkan persegi panjang bulat di sekitar ikon layar beranda + Warna LED Pemberitahuan + Daya Mati Otomatis + Saldo Edy + Peringatan + Margin Alarm Pintar + Gunakan Desain Kaya + Hari Dimulai Pada + Pengaturan Pemberitahuan Per Aplikasi + Tidak Ada Getaran + Kuat + Tiga Kali + Layar Aktivitas + Pengaturan Pemberitahuan + Sony WF-1000XM5 + Sembunyikan bodi saja + Latvia + Simbol Umum + Abaikan notifikasi profil kerja + Jangan kirimkan notifikasi dari aplikasi dalam profil kerja ke jam tangan \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 206293670..d762c02c9 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -963,7 +963,7 @@ m Piano Giri - Stile di nuoto + Stile Nuoto Camminata media Ritmo Battito cardiaco @@ -1230,7 +1230,7 @@ Abilita se il tuo dispositivo non si riesce piÚ a connettere dopo un aggiornamento del firmware Nega le notifiche dalle applicazioni selezionate Impostazioni Notifiche - Nascondi contenuto + Nascondi tutto il contenuto Barometro Torcia Alcuni file(s) esistono già. Sovrascrivere\? @@ -1271,7 +1271,7 @@ Equalizzatore Utilizza la lista delle applicazioni perâ€Ļ Modalità privacy per i messaggi - Mostra contenuto + Mostra tutto il contenuto Configura promemoria Spazio esaurito Il dispositivo non ha spazio libero per aggiungere altri promemoria (totali: %1$s) @@ -2374,4 +2374,81 @@ \nPROCEDI A TUO RISCHIO E PERICOLO! Amazfit Cheetah (Quadrato) SPERIMENTALE + Bianco puro + Auricolari + Modalità normale (60s-90s) + Il dispositivo non dispone di spazio libero sufficiente + Sinistra + L\'MTU attuale di %1$d è troppo basso, attiva un MTU elevato nelle impostazioni del dispositivo e scollega/ricollega il dispositivo. + Impostazioni Attività + Bianco + Modalità rapida (30s) + Ignora notifiche del profilo di lavoro + Destra + Impostazioni Schermo + Modalità di misurazione + Dimensione Carattere piÚ grande + Aumenta la dimensione dei caratteri nel calendario, notifiche, ecc. + Invia testo al dispositivo + Modalità precisa (3min) + Verde + Giallo + Scorciatoie allenamento + Rosso + Ciano + Scorciatoie app + Non inviare all\'orologio le notifiche delle app del profilo di lavoro + Lettone + Centro + Termometro + Utilizzare Design Ricco + Simboli Comuni + Viola + Blu + L\'icona Impostazioni è sempre visualizzata alla fine + Struttura menu: %s + Calorie + Vibrazione Intelligente + Nascondi solo il corpo + Consenti a Wena di chiedere periodicamente a Gadgetbridge di scaricare i dati di attività dal dispositivo. + Media + Struttura menu JSON invalida + Contapassi + Mi Band HRX + Recupero statistiche + Ora Corrente + Sincronizzazione Dati Attività in Background + Debole + Icone Schermata Iniziale + Bohemic Smart Bracelet + Cielo stellato + Amazfit Balance + Meteo Nella Barra Di Stato + Centigradi + Energia Corporea + Accendi il display quando guardi il polso + Reimposta struttura menu + Doppia Pressione + Amazfit Falcon + Amazfit Active Edge + Recupero dati temperatura + Mostra le condizioni meteo attuali nell\'angolo in alto a sinistra della schermata iniziale + Sony Wena 3 + Icone Menu + Media ossigeno nel sangue + Fahrenheit + Intensità Vibrazione + Pressione Prolungata + Pagamento + Impostazioni Sveglia + Struttura menu rimossa + Aggiungi rettangoli arrotondati intorno alle icone della schermata iniziale + Ricompila il quadrante dell\'orologio per il menu personalizzato + Amazfit Active + Struttura menu JSON impostata in GB + Forte + Schermta Attività + Impostazioni Notifiche + Sony WF-1000XM5 + Femometer Vinca II \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 4b2b5bfae..f6669efba 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -754,4 +754,86 @@ バãƒŗドでäŊŽåŧˇåēĻぎ振動を有劚ãĢする įœé›ģ力ãƒĸãƒŧドでは、åŋƒæ‹æ•°ãŽåŽšæœŸįš„ãĒč‡Ē動æ¸Ŧ厚がã‚ĒフãĢãĒり、äŊœæĨ­æ™‚é–“ãŒé•ˇããĒりぞす ゚マãƒŧトã‚ĸナãƒŧム間隔は、イãƒŗ゚トãƒŧãƒĢされたã‚ĸナãƒŧムぎ前ぎ間隔です。 こぎ間隔では、デバイ゚はãƒĻãƒŧã‚ļãƒŧをį›ŽčĻšã‚ã•ã›ã‚‹ãŸã‚ãĢįĄįœ ãŽæœ€ã‚‚čģŊいæŽĩ階を検å‡ēしようとしãĻいぞす + į€äŋĄč¨­åŽš + č¨­åŽšä¸­ã‚ŋイマãƒŧを開始 + č¨­åŽšã‚ĸイã‚ŗãƒŗは常ãĢãƒĄãƒ‹ãƒĨãƒŧぎ最垌ãĢ襨į¤ēされぞす + 3回 + Qrioロック + é›ģæēã‚Ēフ + ã‚ˇãƒ§ãƒŧト + ã‚ĢロãƒĒãƒŧ + åˇĻ + į‚šį¯ãĒし + ゚テップãƒģダã‚Ļãƒŗ + ゚マãƒŧトバイブ + Riiiver + Wenaから厚期ぎ同期化čĻæą‚を受äŋĄã—た際ãĢGadgetbridgeãĢã‚ĸクテã‚Ŗビテã‚Ŗデãƒŧã‚ŋを同期化しぞす + Suica掋éĢ˜ + 晎通 + ã‚ĸクテã‚Ŗビテã‚Ŗč¨­åŽš + é€Ŗįļš + æ­Šæ•° + Qrio + į™Ŋ + įžåœ¨æ™‚åˆģ + č‡Ē動デãƒŧã‚ŋ同期化 + åŧą + ホãƒŧムį”ģéĸä¸Ļãŗæ›ŋえ + ゚テãƒŧã‚ŋ゚バãƒŧãĢ夊気äēˆå ą + ゚テップãƒģã‚ĸップ + åŗ + ベãƒŧã‚ˇãƒƒã‚¯ + į”ģéĸ襨į¤ēč¨­åŽš + フりãƒŗトã‚ĩイã‚ē大きめ + エナジãƒŧ + 2回 + 指厚した時間でWenaぎé›ģæēã‚’č‡Ē動でã‚Ēフãƒģã‚ĒãƒŗãĢしぞす + 腕éĻ–を回すときį”ģéĸをį‚šį¯ã—ぞす + 通įŸĨã€ã‚šã‚ąã‚¸ãƒĨãƒŧãƒĢãĒおぎ文字を大きくãĢしぞす + ダブãƒĢクãƒĒック + ナピッド + Qrioč§Ŗ除 + 一回 + į€äŋĄé€šįŸĨを受け取る + ホãƒŧムį”ģéĸぎåˇĻ上ãĢ夊気ã‚ĸイã‚ŗãƒŗã‚’čĄ¨į¤ēしぞす + Wena Pay + ボã‚ŋãƒŗ操äŊœ + é›ģæēã‚Ēãƒŗ + į€äŋĄãƒã‚¤ãƒ– + įˇ‘ + éģ„ + į€äŋĄã¨ããŽLED色 + čĩ¤ + 青įŠē + ã‚ĩイãƒŦãƒŗ + ãƒĄãƒ‹ãƒĨãƒŧä¸Ļãŗæ›ŋえ + į€äŋĄãƒã‚¤ãƒ–įš°ã‚Ščŋ”し + 通įŸĨぎバイブ + ボã‚ŋãƒŗを操äŊœã™ã‚‹ãžã§ + 4回 + バイブåŧˇã• + į„ĄåŠšãĢするとį€äŋĄé€šįŸĨはWenaãĢåąŠã‹ãĒくãĒる + 長æŠŧし + 支払い + ã‚ĸクテã‚Ŗビテã‚Ŗį”ģéĸä¸Ļãŗæ›ŋえ + ã‚ĸナãƒŧãƒ č¨­åŽš + 通įŸĨぎバイブįš°ã‚Ščŋ”し + 中 + ホãƒŧムį”ģéĸぎã‚ĸイã‚ŗãƒŗãĢčƒŒæ™¯ã‚’čĄ¨į¤ēしぞす + 通įŸĨときぎLED色 + č‡Ē動é›ģæēã‚Ēフãƒģã‚Ēãƒŗ + Edy掋éĢ˜ + ワãƒŧニãƒŗグ + ゚マãƒŧトã‚ĸナãƒŧムäŊ™č¨ˆ + ãƒĒッチデã‚ļイãƒŗ + デãƒŧã‚ŋ送り時間 + ã‚ĸプãƒĒåˆĨ通įŸĨč¨­åŽš + バイブãĒし + į´Ģ + åŧˇ + トãƒĒプãƒĢ + 青 + ã‚ĸクテã‚Ŗビテã‚Ŗį”ģéĸ + 通įŸĨč¨­åŽš + Bangle.js Gadgetbridge(Nightly) \ No newline at end of file diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e2eccdf8e..0ad3b7b5c 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -304,7 +304,7 @@ ė œ 3ėž ė•ąėœŧ로 ëŗ´ë‚´ëŠ” 메ė„¸ė§€ė— ė–¸ė œë‚˜ 바로 ACKė„ ëŗ´ëƒ…니다 단ėœ„ ė‹œę°„ 형ė‹ - ė—°ę˛° + ė—°ę˛°â€Ļ ė§€ę¸ˆ 당ė‹ ė˜ Amazfit Corė— %s 펌ė›¨ė–´ëĨŧ ė„¤ėš˜í•˜ë ¤ęŗ  합니다. \n \n.fw 파ėŧė„ ė„¤ėš˜í•˜ęŗ  .res 파ėŧė„ ė„¤ėš˜í•œ 후 ėĩœėĸ…ė ėœŧ로 .gps 파ėŧė„ ė„¤ėš˜í•˜ė„¸ėš”. .fw 파ėŧė„ ė„¤ėš˜í•œ 후 기기가 ėžŦė‹œėž‘ 됩니다. @@ -611,4 +611,43 @@ ė ˆė „ ëĒ¨ë“œëŠ”ėŖŧ기ė ė¸ ė‹Ŧ박ėˆ˜ ėžë™ ė¸Ąė •ė„ 해ė œí•˜ė—Ŧ ėž‘ė—… ė‹œę°„ė„ 늘ëĻŊ니다. ëļ„ 단ėœ„ė˜ ėŠ¤ë§ˆíŠ¸ ė•ŒëžŒ 간격 ėŠ¤ë§ˆíŠ¸ ė•ŒëžŒ 간격ė€ ė„¤ėš˜ëœ ė•ŒëžŒ ė´ė „ė˜ 간격ėž…니다. ė´ 간격ė—ė„œ ėžĨėš˜ëŠ” ė‚ŦėšŠėžëĨŧ ꚨėš°ę¸° ėœ„í•´ 가ėžĨ 가ë˛ŧėš´ ėˆ˜ëŠ´ 단ęŗ„ëĨŧ 감ė§€í•˜ë ¤ęŗ í•Šë‹ˆë‹¤. + ė „ė› 끄기 + ėŧ + 2ėŖŧ + ė •í™•ë„ + Gadgetbridgeė— 대하ė—Ŧ + Bangle.js Gadgetbridge + Bangle.js Gadgetbridge + Bangle.js Gadgetbridgeė— 대하ė—Ŧ + ė „ė› 끄기 + 기기ė˜ ė „ė›ė„ 끄ęŗ  ė‹ļė€ 것ė´ 확ė‹¤í•œę°€ėš”? + 배터ëĻŦ 레벨 + ėŖŧ + ëŗ„ëĒ… ė„¤ė • + ėˆ˜ëŠ´ + ęą°ëĻŦ + 활ė„ą 깸ėŒ + ė‹Ŧ박ėˆ˜ ė¸Ąė • ė–ģ기 + 배터ëĻŦ ė •ëŗ´ + ė ˆė „ + ėģ¤ėŠ¤í…€ + ėˆ¨ę¸°ę¸° + 활동 ëĒŠëĄ + ëŗ€ę˛Ŋ 기록 + 균형 + ė €ė „ë Ĩ GPS + GPS + GPS + BDS + GPS + GNOLASS + GPS + GALILEO + ėˆ˜ëŠ´ 기간 표ė‹œ + ęą°ëĻŦ는 깸ėŒęŗŧ ëŗ´í­ė—ė„œ ęŗ„ė‚°ëŠë‹ˆë‹¤ (ė„¤ė •ė—ė„œ ëŗ€ę˛Ŋ 가ëŠĨ) + ė „ė˛´ 깸ėŒ ėˆ˜ 표ė‹œ + ë‹Ŧ + 3ë‹Ŧ + 6ë‹Ŧ + 년 + ė‹œėž‘ + 활ė„ą ė‹œę°„ + 활동 \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 952fa0298..ebc35458d 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -50,7 +50,7 @@ Als u nog steeds wilt doorgaan en uw apparaat blijft goed functioneren, vertel dan alstublieft de ontwikkelaars van Gadgetbridge om de %s firmware-versie op de goedgekeurde lijst te zetten. Instellingen Algemene instellingen - Verbind met Gadgetbridge-apparaat wanneer Bluetooth wordt ingeschakeld + Verbind met Gadgetbridge-apparaten wanneer Bluetooth wordt ingeschakeld Start automatisch Verbind automatisch opnieuw Gewenste audiospeler @@ -141,7 +141,7 @@ \nOpmerking: U hoeft de .res niet te installeren als deze exact dezelfde is als degene die ervoor al was geïnstalleerd. \n \nGA VERDER OP EIGEN RISICO! - Activeer links/rechts vegen in grafiek schermen + Activeer links/rechts vegen in grafiekschermen Weer Weerlocatie (voor LinageOS-weeraanbieder) Verwijder automatisch verworpen meldingen @@ -580,7 +580,7 @@ Huidige / Max hartslag: %1$d / %2$d Nacht modus Verlaag \'s nachts automatisch de scherm helderheid - Grafiek instellingen + Grafiekinstellingen Maximum hartslag Minimum hartslag OK @@ -673,7 +673,7 @@ Anti-verlies waarschuwing elke 15 minuten elke 45 minuten - Dagelijks doel: calorieÃĢn verbrandt + Dagelijks doel: calorieÃĢn verbrand Dagelijks doel: afstand in meters Dagelijks doel: tijd actief in minuten Mi Scale 2 @@ -712,7 +712,7 @@ Ondergrens Bovengrens Gemiddelde: %1$s - Grafiek instellingen + Grafiekinstellingen Toon gemiddelden in de grafieken Grafiekbereik Grafiekbereik ingesteld op 1 maand @@ -996,8 +996,8 @@ Dalend Stijgend Rondes - swimStyle - swolfIndex + Zwemstijl + SWOLF Je staat op het punt om de %s -firmware op je Amazfit Verge Lite te installeren. \n \nZorg ervoor dat je het .fw-bestand installeert, vervolgens het .res-bestand en tenslotte het .gps-bestand. Je horloge zal opnieuw opstarten nadat je het .fw-bestand hebt geïnstalleerd. @@ -1334,8 +1334,8 @@ Privacymodus berichten Deselecteer alle apps Gebruik de app-lijst voorâ€Ļ - Toon inhoud - Verberg inhoud + Toon alle inhoud + Verberg alle inhoud App-lijst App moet geselecteerd zijn om ingesteld te worden Galaxy Buds @@ -2268,11 +2268,11 @@ OK Titel %d min - PAI totaal + Totaal PAI per week +%d PAI per maand - Dag PAI-toename + Dag-toename Catima-pakketnaam Gearchiveerde kaarten synchroniseren Alleen kaarten met ster synchroniseren @@ -2399,4 +2399,408 @@ Frans (Frankrijk) Het apparaat heeft niet genoeg vrije ruimte Stuur tekst naar apparaat + Huidige MTU van %1$d is te laag, activeer alstublieft Hoge MTU in de apparaatinstellingen en verbind het apparaat opnieuw. + Mi Band HRX + Schakel het nieuwe scan-scherm in, die de problemen met het vinden van apparaten zou moeten oplossen. Schakel dit uit wanneer u problemen ondervindt met het vinden en koppelen van uw apparaat. + Nieuw scan-scherm inschakelen + Instellingen binnenkomend gesprek + Start timer + Instellingen wordt altijd aan het eind getoond + Drie keer + Qrio blokkeren + Uitschakeltijd + Kort + CaloriÃĢn + Links + Geen LED + Stap omlaag + Slimme trilling + Riiiver + Sta de Wena toe om periodiek Gadgetbridge te vragen om activiteitsgegevens te downloaden van het apparaat + Suica-saldo + Middelmatig + Activiteitsinstellingen + Doorgaand + Stappenteller + Qrio + Wit + Huidige tijd + Synchronisatie activiteiten op de achtergrond + Zwak + Startscherm-iconen + Weer in statusbalk + Celsius + Stap omhoog + Rechts + Basis + Scherminstellingen + Groter lettertype + Energie + Twee keer + Het apparaat zal automatisch uit- en inschakelen op het aangegeven schema + Schakel het scherm in wanneer u naar uw pols kijkt + Gebruik een groter lettertype in de kalender, meldingen, etc. + Dubbel indrukken + Snel + Qrio deblokkeren + Temperatuurschaal + Eenmalig + Ontvang telefoongesprekmeldingen + Toon het huidige weer in de hoek linksboven op het startscherm + Wena Pay + Knopactie + Inschakeltijd + Trilling binnenkomend telefoongesprek + Groen + Geel + Sony Wena 3 + LED-kleur binnenkomend telefoongesprek + Rood + Cyaan + Sirene + Selecteer welke temperatuurschaal het apparaat gebruikt. + Menu-iconen + Herhaling trilling binnenkomend telefoongesprek + Trilling meldingen + Oneindig + Vier keer + Fahrenheit + Trillingssterkte + Wanneer de Wena uitgeschakeld is krijgt u geen meldingen van telefoongesprekken + Lang indrukken + Betaling + Sortering statusscherm + Alarminstellingen + Herhaling trilling meldingen + Midden + Toon afgeronde rechthoeken rond iconen op het startscherm + LED-kleur meldingen + Automatisch uitschakelen + Edy-saldo + Waarschuwing + Marge slim alarm + Gebruik luxe ontwerp + Dag start om + Per-app meldingsinstellingen + Geen trilling + Paars + Sterk + Driedubbel + Blauw + Activiteitenscherm + Meldingsinstellingen + Sony WF-1000XM5 + Verberg alleen tekst + Lets + Negeer meldingen uit het werkprofiel + Stuur geen meldingen van apps in het werkprofiel naar het apparaat + Algemene symbolen + Puur wit + Negeren (dempen) + Gemiddelde slagsnelheid + Het uiterste + Stad van snelheid + Koptelefoon + Normaal (60s-90s) + Afwijzen + Vrije combinatie + Lichaamssamenstelling + Zepp Pay + Telefoongesprek-afwijsmethode + Bezig met ophalen statistieken + Snel (30s) + Sterrenhemel + Amazfit Balance + Navigatie gestart maar navigationApp is niet op het horloge geïnstalleerd. Installeer het via de App-beheerder. + yard + slagen/min + Meetmodus + Welke actie wordt genomen wanneer een inkomend telefoongesprek wordt geweigerd op het horloge + Bezig met ophalen temperatuurgegevens + Precies (3min) + Enorme hemel + Trainingssnelkoppelingen + Maximale slagsnelheid + Bliksemschicht + Baanlengte + Totaal aantal slagen + App-snelkoppelingen + Gids + Gereedheid + Thermometer + Femometer Vinca II + Navigatie-app niet geïnstalleerd op horloge + Amazfit Active Edge + Zuurstofsaturatie + Amazfit Active + Navigatie-apps + Navigatie-instellingen + Google Maps + Plaats app-naam vooraan meldingen + Selecteer met welke versie van OsmAnd verbonden moet worden + App-naam in melding + OsmAnd-pakketnaam + OsmAnd(+) + Overige meldingen + Kalendermeldingen + Tril/piep voor binnenkomende telefoongesprekken + Xiaomi Smart Band 8 + Hardlopen + Planning slaapmodus + Melding voor telefoongesprekken + Bedtijd + Melding voor nieuwe e-mail + Tril/piep bij e-mailmeldingen + Focus + Tril/piep voor SMS-meldingen + Toon een preview van het bericht in de titel + Redmi Watch 3 Active + Tril/piep voor kalendermeldingen + Serienummer + Deens + Wektijd + Statistieken + Tril/piep voor meldingen in de categorie Overig + Xiaomi Watch Lite + Laat een preview van het bericht zien in de titel van een melding voor zover toegestaan door het apparaat + Stuur een melding en start slaapmodus op de ingestelde bedtijd. Op de ingestelde wektijd klinkt een alarmsignaal. + Melding voor SMS + Meldingen + Xiaomi Smart Band 7 Pro + Xiaomi Watch S1 Active + Mi Watch Color Sport + Pixoo + Stuur app-meldingen naar het apparaat + Stuur meldingen + Niet storen - Aan + Stiltestand telefoon + Draagwijze + Pebble (schoengesp) + Normaal / Trillen + Tweede doel + Krijg een melding wanneer je vitaliteitsscore 30, 60 of 100 bereikt in de afgelopen 7 dagen + Vitaliteitsscore + Niet storen - Alleen alarmen + Trillen / Stil + Krijg een melding wanneer je het dagelijks maximum aantal vitaliteitspunten hebt bereikt + Niet storen - Alleen belangrijk + Niet ingesteld + Tijd staand + Tijd actief + Band (armband) + Ketting (om de nek) + Dagelijkse voortgang + 7-daagse voortgang + Niet storen - Uit + Normaal / Stil + Trail run + Widget-layout + Het apparaat heeft geen vrije plekken voor widget-schermen (totaal aantal plekken: %1$s) + Onbekende training - %s + Omhoog + Weet u zeker dat u \'%1$s\' wilt verwijderen? + Klap in handen voor scherm aan" + Wandeltocht + 2 widgets + Verwijder widget-scherm + Klap nog een keer voor scherm uit" + 1 boven, 2 onder + ColaCao 2023 + 2 boven, 2 onder + Er moeten minimaal %1$s schermen zijn + Widget + Redmi Smart Band 2 + Widget-scherm + Omlaag + 1 widget + Widget-subtype + Scherm %s + Selecteer alle widgets + 2 boven, 1 onder + ColaCao 2021 + Het scherm zal uitgaan nadat de microfoon een tijd geen geluid heeft opgevangen + Worstelen + Mijia thermo- en hygrometer 2 + Navigatie-instructies + Configureer gedrag van navigatie-app op horloge + Mag de navigatie-app automatisch naar de voorgrond komen als er een navigatie-instructie ontvangen wordt? + Tril bij nieuwe instructie + Naar voorgrond + Apparaatnaam + Moet het horloge trillen bij elke nieuwe of veranderde navigatie-instructie (alleen als de app op de voorgrond is)? + Redmi Watch 2 Lite + Connectie status + Redmi Smart Band Pro + Nothing Ear (2) + Alleen wanneer scherm-aan-bij-oppakken is ingeschakeld + Niet storen wanneer het apparaat niet gedragen wordt + Handmatig + Hele dag + Honor Band 3 + Honor Band 4 + Honor Band 5 + Honor Band 6 + Honor Band 7 + Huawei Band (AW70) + Huawei Band 6 + Huawei Band 7 + Huawei Band 8 + Huawei Watch GT + Huawei Band 4 (Pro) + Huawei Watch GT 2 (Pro) + Huawei Watch GT 2e + Huawei Talk Band B6 + Huawei Watch GT 3 (Pro) + Lichte actieve geluidsonderdrukking + Transparantie + Verwijder het vinkje niet bij slim wakker worden. + HUAWEI TruSleep â„ĸ + Verbeterde slaapmonitoring + Telefoontjes accepteren inschakelen + Het op het apparaat accepteren van binnenkomende telefoongespreken inschakelen + Telefoontjes weigeren inschakelen + Het op het apparaat weigeren van binnenkomende telefoongespreken inschakelen + Vind mijn telefoon uitschakelen wanneer Niet Storen actief is + Automatische hartslagmeting inschakelen + Automatische bloedzuurstofmeting inschakelen + Melding op het apparaat bij verbroken Bluetooth-verbinding. + Nothing Ear (Stick) + Werkmodus + Plaats geen vinkje bij slim wakker worden. + Monitor uw slaapkwaliteit en adempatroon in realtime. +\nAnalyseer uw slaappatronen en diagnosticeer 6 types slaapproblemen. + Opties forceren + Sommige apparaten geven onterecht aan sommige opties niet te ondersteunen. Deze instellingen gebruikt worden om ze alsnog in te schakelen. +\nGEBRUIK OP EIGEN RISICO +\nLees de documentatie + Forceer slim alarm + Forceer ondersteuning van slimme alarmen +\nGEBRUIK OP EIGEN RISICO + Maak alleen opnieuw verbinding met verbonden apparaten + Maak alleen opnieuw verbinding met verbonden apparaten, in plaats van met alle apparaten + Het batterijniveau van het apparaat is te laag + Forceer draaglocatie + Forceer ondersteuning voor Niet Storen + Negeer startstatus wakkerworden + Kan helpen voor correcte slaapdetectie. Direct zichtbaar in het dagelijkse activiteitenoverzicht. + Negeer eindstatus wakkerworden + Kan helpen voor correcte slaapdetectie. Direct zichtbaar in het dagelijkse activiteitenoverzicht. + Forceer ondersteuning voor draaglocatie +\nGEBRUIK OP EIGEN RISICO + Forceer ondersteuning voor Niet Storen +\nGEBRUIK OP EIGEN RISICO + Trainingsgegevens opnieuw analyseren + Stuur een foutopsporingsverzoek naar het Huawei-apparaat + Foutopsporingsverzoek + Dit zal alleen iets doen na bepaalde updates + Zoemerintensiteit + Afstand zonder hoogteverschil + Versie 2 + Versie 3 + Protocolversie + Het wachtwoord moet uit 4 cijfers bestaan + Xiaomi Smart Band 8 Pro + Mijia MHO-C303 + Versie 1 + Xiaomi Watch S1 + Xiaomi Watch S3 + Xiaomi Watch S1 Pro + Wijzerplaat uploadenâ€Ļ + Wijzerplaat uploaden + Wijzerplaat-installatie gelukt + Wijzerplaat-installatie mislukt + Forceer verbindingstype + U kunt proberen het type verbinding te forceren indien het Gadgetbridge niet lukt om verbinding te maken + Automatisch + Bluetooth LE + Bluetooth (klassiek) + Activiteitsinformatie + 155 bpm + 165 bpm + 175 bpm + 185 bpm + 195 bpm + 205 bpm + Schakel drinkherinneringen tijdelijk uit + Binnen hardlopen + Bergwandelen + Veldlopen + Karate + Fitnessoefeningen + Crossfit + Jazz-dansen + Latin-dansen + Jetski + Skaten + Ballet + Overige dansen + Sneeuwsporten + Redmi Watch 2 + CMF Watch Pro + Meldingsdrempel maximale hartslag + Crosstrainer + Vrije training + Roeimachine + Dynamisch fietsen + Traploop-apparaat + Functionele training + Fysieke training + Taekwondo + Schermen + Kendo + Rekstok + Brug met gelijke leggers + Afkoelen + Crosstraining + Sit-ups + Fitness-gamen + Aerobics + Rollen + Rekken + Atletiek + Push-ups + Battle rope + Smith machine + Pull-ups + Planken + Speerwerpen + Verspringen + Hoogspringen + Trampoline + Dumbbell + Buikdansen + Rolschaatsen + Vechtsporten + Tai chi + Hoelahoep + Discuswerpen + Darten + Boogschieten + Paardrijden + Kiten + Schommelen + Traplopen + Vissen + Handfietsen + Geest en lichaam + Kabaddi + Karten + Biljard + Badminton + Softbal + Trefbal + Australisch football + Pickleball + Lacrosse + Shot + Zeilen + IJshockey + Curling + Langlaufen + Skateboarden + Rotsklimmen + Jagen + Buiten wandelen + Sony WI-SP600N + Honor MagicWatch 2 \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 543ef8fdd..5804dd6b2 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -39,13 +39,13 @@ Ustawienia Ustawienia ogÃŗlne - Połącz z urządzeniem Gadgetbridge, gdy Bluetooth jest włączony + Połącz urządzenia z Gadgetbridge, gdy Bluetooth jest włączony Automatycznie ponÃŗw połączenie Preferowany odtwarzacz muzyki Domyślny Data i godzina Synchronizuj czas - Synchronizuj czas urządzenia Gadgetbridge podczas łączenia lub gdy czas albo strefa czasowa zmienią się na urządzeniu Android + Synchronizuj czas urządzenia podczas łączenia lub gdy czas albo strefa czasowa zmienią się na urządzeniu Android Motyw Jasny Ciemny @@ -105,14 +105,14 @@ Kliknij połączone urządzenie, aby uruchomić menadÅŧer aplikacji Dotknij urządzenie aby połączyć Nie moÅŧna połączyć. Nieprawidłowy adres Bluetooth\? - Gadgetbridge działa + Gadgetbridge jest uruchomiony Instalowanie binarki %1$d/%2$d Instalacja nie powiodła się Zainstalowano - PRÓBUJESZ ZAINSTALOWAĆ FIRMWARE, POSTĘPUJESZ NA WŁASNĄ ODPOWIEDZIALNOŚĆ . -\n -\n -\n Ten firmware jest przeznaczony dla wersji HW: %s + PRÓBUJESZ ZAINSTALOWAĆ OPROGRAMOWANIE UKŁADOWE, KONTYNUUJ NA WŁASNE RYZYKO. +\n +\n +\n Te oprogramowanie układowe jest przeznaczone dla wersji HW: %s Zamierzasz zainstalować poniÅŧszą aplikację: \n \n%1$s @@ -220,7 +220,7 @@ KrokÃŗw dziś, cel: %1$s Nie przesyłaj danych aktywności ACK Jeśli transmisja danych nie zostanie potwierdzona przez opaskę, dane dotyczące aktywności nie zostaną wyczyszczone. Przydatne, jeśli GB jest uÅŧywany razem z innymi aplikacjami. - Dane aktywności będą zachowane na urządzeniu nawet po synchronizacji. Jest to przydatne, gdy Gadgetbridge jest uÅŧywany razem z innymi aplikacjami. + Dane aktywności będą zachowane na urządzeniu nawet po synchronizacji. Jest to przydatne, gdy Urządzenia na ciele jest uÅŧywany razem z innymi aplikacjami. MoÅŧe pomÃŗc na urządzeniach gdzie aktualizacja kończy się błędem. Historia krokÃŗw Aktualnie krokÃŗw/min @@ -498,7 +498,7 @@ Nie zmierzono Aktywność Urządzenie nie noszone - Chodzenie + Spacer Nieznana aktywność Aktywności sportowe Jazda na rowerze @@ -519,7 +519,7 @@ Godziny: Sekundy: Kalibruj - Prosimy pamiętać, Åŧe logi Gadgetbridge mogą zawierać wiele informacji osobistych, w tym dane dotyczące zdrowia, unikalne identyfikatory (takie jak adresy MAC urządzeń), preferencje muzyczne itd. RozwaÅŧ edycję logÃŗw i usunięcie takich informacji zanim wyślesz je przy zgłaszaniu problemu. + Prosimy pamiętać, Åŧe logi Gadgetbridge mogą zawierać wiele informacji osobistych, w tym dane dotyczące zdrowia, unikalne identyfikatory (takie jak adresy MAC urządzeń), preferencje muzyczne itd. RozwaÅŧ edycję logÃŗw i usunięcie takich informacji, zanim wyślesz je przy zgłaszaniu problemu. OstrzeÅŧenie! Brak danych Kolor LED @@ -541,7 +541,7 @@ Koniec Odblokowanie ekranu opaski Przesuń w gÃŗrę, aby odblokować ekran opaski - Norwegian BokmÃĨl + Norweski BokmÃĨl Przeniesienie danych z okresu %1$s Ustawienie alarmu na %1$02d:%2$02d Błąd tworzenia folderu na pliki log: %1$s @@ -682,7 +682,7 @@ Sen w miesiącu Mijia Smart Clock NFC - Zezwala innym aplikacjom na dostęp do danych HR w czasie rzeczywistym, gdy Gadgetbridge jest połączony + UmoÅŧliwia innym aplikacjom dostęp do danych HR w czasie rzeczywistym, gdy Gadgetbridge jest podłączony UÅŧyj niestandardowej czcionki Włącz tę opcję, jeśli Twoje urządzenie ma niestandardowy firmware czcionek do obsługi emoji Automatyczny eksport @@ -845,7 +845,7 @@ Wymuś schemat kolorÃŗw czarno na białym UÅŧyteczne, jeśli twÃŗj zegarek ma ciemne wskazÃŗwki Wysoki priorytet - PokaÅŧ specyficzną dla urządzenia ikonę powiadomienia zamiast ikony Gadgetbridge, gdy połączono + PokaÅŧ specyficzną dla urządzenia ikonę powiadomienia kiedy zostanie połączone zamiast ikony Gadgetbridge Trening Stoper Siła wibracji @@ -903,7 +903,7 @@ Ostatnie powiadomienie Ustaw własną nazwę (alias) WspÃŗłtwÃŗrcy - O Gadgetbridge + Informacje o Gadgetbridge Amazfit T-Rex Styl grzbietowy Styl dowolny @@ -1021,7 +1021,7 @@ Linki Dziękujemy wszystkim nie notowanym na liście wspÃŗłtwÃŗrcom za udostępnienie kodu, tłumaczenia, wsparcie, pomysły, motywację, zgłoszenia błędÃŗw, pieniądzeâ€Ļ ✊ ZespÃŗł głÃŗwny (w kolejności od pierwszego wkładu w kod aplikacji) - Niewymagający chmury wolny zamiennik dla zamkniętoÅērÃŗdłowych aplikacji dostarczanych przez producentÃŗw gadÅŧetÃŗw na system Android. + Niewymagający chmury wolny zamiennik dla zamknięto ÅērÃŗdłowych aplikacji dostarczanych przez producentÃŗw gadÅŧetÃŗw na system Android. Edytuj etykietę Ping-pong Krykiet @@ -1087,7 +1087,7 @@ Urządzenie jest połączone Urządzenie się łączy ruchy/s - swolfIndeks + SWOLF Nut mini Wszystkie urządzenia Poprzedni miesiąc @@ -1178,7 +1178,7 @@ Amazfit GTS 2e Informacje o baterii Amazfit X - Właśnie zamierzasz zainstalować oprogramowanie układowe %s na swoim Amazfit X. + Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Twoim Amazfit X. \n \nProszę upewnić się, Åŧe zainstalowany został plik .fw, a następnie plik .res. Po zainstalowaniu pliku .fw Twoja opaska zostanie ponownie uruchomiona. \n @@ -1208,7 +1208,7 @@ NiektÃŗre funkcje są wyłączone, poniewaÅŧ oprogramowanie zegarka jest zbyt nowe Skonfiguruj, kiedy urządzenie będzie emitować sygnał dÅēwiękowy DÅēwięki - Właśnie zamierzasz zainstalować oprogramowanie układowe %s na swoim Amazfit Neo. + Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Twoim Amazfit Neo \n \nPo zainstalowaniu pliku .fw Twoja opaska zostanie ponownie uruchomiona. \n @@ -1345,9 +1345,9 @@ Zaznacz wszystkie aplikacje UÅŧyj listy aplikacji, abyâ€Ļ Tryb prywatności wiadomości - Wyświetlaj zawartość + Wyświetl całą zawartość Odrzucaj powiadomienia z wybranych aplikacji - Ukryj zawartość + Ukryj całą zawartość Galaxy Buds Ustawienia wstępne korektora dÅēwięku Podbicie basÃŗw @@ -1611,19 +1611,19 @@ Tekst jako bitmapy ZezwÃŗl na dostęp do internetu ZezwÃŗl aplikacjom na tym urządzeniu na dostęp do Internetu - 100 uderzeń + 100 uderzeń/min Usuń \'%1$s\' Strefa czasowa - 105 uderzeń - 110 uderzeń + 105 uderzeń/min + 110 uderzeń/min 112 bpm - 120 uderzeń - 125 uderzeń - 130 uderzeń - 135 uderzeń - 140 uderzeń - 145 uderzeń - 150 uderzeń + 120 uderzeń/min + 125 uderzeń/min + 130 uderzeń/min + 135 uderzeń/min + 140 uderzeń/min + 145 uderzeń/min + 150 uderzeń/min Spotify 5 sekund 10 sekund @@ -1649,7 +1649,7 @@ Kalendarz Zdrowie Czas - 40 uderzeń + 40 uderzeń/min 45 uderzeń/min 50 uderzeń/min 80% @@ -1696,11 +1696,11 @@ Automatyczne monitorowanie poziomu tlenu we krwi przez cały dzień PrÃŗg alarmowy SPO2 Monitorowanie jakości oddechu podczas snu - Zamierzasz zainstalować oprogramowanie %s na swoim Xiaomi Smart Band 7. + Zamierzasz zainstalować oprogramowanie układowe %s na swoim Xiaomi Smart Band 7. \n \nTwoja opaska uruchomi się ponownie po zainstalowaniu pliku .zip. \n -\nROBISZ TO NA WŁASNĄ ODPOWIEDZIALNOŚĆ! +\nKONTYNUUJ NA WŁASNE RYZYKO! Proszę podłączyć TYLKO JEDNO urządzenie, do ktÃŗrego chcesz wysłać plik. Połączenie Wyświetlanie @@ -1719,16 +1719,16 @@ Aplikacja towarzysząca dla Bangle.js, zbudowana na bazie projektu Gadgetbridge, z dodanym dostępem do Internetu. \n \nZe względu na politykę Sklepu Google Play, nie moÅŧemy umieścić linku do darowizny w samej aplikacji, ale jeśli Ci się ona podoba, rozwaÅŧ przekazanie darowizny poprzez stronę głÃŗwną Gadgetbridge poniÅŧej. - Gadgetbridge (Wczesna) - Gadgetbridge Wczesna - O Gadgetbridge Nightly - Niewymagający chmury wolny zamiennik dla zamkniętoÅērÃŗdłowych aplikacji dostarczanych przez producentÃŗw gadÅŧetÃŗw na system Android. Wydania Nightly Gadgetbridge. Nie moÅŧna go zainstalować, jeśli masz juÅŧ zainstalowaną aplikację Gadgetbridge lub Pebble, ze względu na konflikt w dostawcy Pebble. - Gadgetbridge (Nightly) działa - Gadgetbridge (Nightly, bez dostawcy Pebble) - Gadgetbridge Nightly Bez Pebble - O Gadgetbridge Nightly Bez Pebble - Niewymagający chmury wolny zamiennik dla zamkniętoÅērÃŗdłowych aplikacji dostarczanych przez producentÃŗw gadÅŧetÃŗw na system Android. Wydania Nightly Gadgetbridge. W tej wersji zmieniono nazwę dostawcy Pebble, aby zapobiec konfliktom, więc niektÃŗre integracje związane z Pebble nie będą działać, ale moÅŧna ją zainstalować obok istniejącej instalacji Gadgetbridge. - Gadgetbridge (Nightly Bez Pebble) działa + Gadgetbridge (Nightly) + Gadgetbridge Nightly + Informacje o Gadgetbridge Nightly + Niewymagający chmury wolny zamiennik dla zamknięto ÅērÃŗdłowych aplikacji dostarczanych przez producentÃŗw gadÅŧetÃŗw na system Android. Wczesne wydanie Gadgetbridge. Nie moÅŧna go zainstalować, jeśli masz juÅŧ zainstalowaną aplikację Urządzenia na ciele lub Pebble, ze względu na konflikt w dostawcy Pebble. + Gadgetbridge (Nightly) jest uruchomiony + Gadgetbridge (Nightly, No Pebble provider) + Gadgetbridge Nightly NoPebble + Informacje o Gadgetbridge Nightly NoPebble + Niewymagający chmury wolny zamiennik dla zamknięto ÅērÃŗdłowych aplikacji dostarczanych przez producentÃŗw gadÅŧetÃŗw na system Android. Wczesne wydanie Gadgetbridge. W tej wersji zmieniono nazwę dostawcy Pebble, aby zapobiec konfliktom, więc niektÃŗre integracje związane z Pebble nie będą działać, ale moÅŧna ją zainstalować obok istniejącej instalacji Gadgetbridge. + Gadgetbridge (Nightly NoPebble) jest uruchomiona Jeśli słowo nie moÅŧe być wygenerowane za pomocą czcionki zegarka, wygeneruj je do bitmapy w Gadgetbridge i wyświetl ją na zegarku Ta funkcja ma potencjał, aby pozostawić z Twojego urządzenia cegłę. To powiedziawszy, taka sytuacja nigdy nie zdarzyła się Åŧadnemu z programistÃŗw poprzez flashowanie, ale pamiętaj, Åŧe robisz to na własne ryzyko. Pobieranie pliku z oprogramowaniem sprzętowym/aplikacją @@ -1752,13 +1752,13 @@ PoniewaÅŧ nie moÅŧemy rozpowszechniać plikÃŗw oprogramowania sprzętowego, będziesz musiał zdobyć je samodzielnie. Oznacza to, Åŧe będziesz musiał szukać plikÃŗw w plikach apk, online, na forach, na Amazfitwatchfaces (dla urządzeń Miband/Amazfit) i tak dalej. Proszę podłączyć PRZYNAJMNIEJ JEDNO urządzenie, do ktÃŗrego chcesz wysłać plik. Inicjalizacja logowania plikÃŗw nie powiodła się, zapisywanie plikÃŗw dziennika jest obecnie niedostępne. Uruchom ponownie aplikację, aby sprÃŗbować ponownie zainicjować pliki dziennika. - Bangle.js Gadgetbridge (Wczesna) - Bangle.js Gadgetbridge (Wczesna) + Bangle.js Gadgetbridge (Nightly) + Bangle.js Gadgetbridge (Nightly) O Bangle.js Gadgetbridge (Nightly) Aplikacja towarzysząca dla Bangle.js, zbudowana na bazie projektu Gadgetbridge, z dodanym dostępem do Internetu. \n \nZe względu na politykę Sklepu Google Play, nie moÅŧemy umieścić linku do darowizny w samej aplikacji, ale jeśli Ci się ona podoba, rozwaÅŧ przekazanie darowizny poprzez stronę głÃŗwną Gadgetbridge poniÅŧej. - Bangle.js (Nightly) działa + Bangle.js jest uruchomiony (Nightly) OpÃŗÅēnienie przed wysłaniem powiadomień o połączeniach przychodzących do urządzenia, w sekundach. Czy na pewno chcesz usunąć zegar światowy\? Alarm tętna (eksperymentalny) @@ -1769,15 +1769,15 @@ NiektÃŗre urządzenia wymagają specjalnego klucza parowania przy pierwszej inicjalizacji urządzenia. Naciśnij tutaj, aby uzyskać więcej szczegÃŗłÃŗw w wiki. (nieaktualne) Rozmiar Bitmapy Tekstu - Jeśli \'Tekst jako bitmapy\' jest włączony, jaki rozmiar powinien mieć generowany tekst\? + Rozmiar uÅŧywany do renderowania tekstu bitmapowego PrÃŗg alarmowy wysokiego tętna Monitorowanie poziomu stresu podczas odpoczynku Ustaw preferencje - PokaÅŧ detale + PokaÅŧ szczegÃŗły Połączono: %d/%d Eliptyczna Błąd przy usuwaniu urządzenia: %s - GPS Gadgetbridge + Gadgetbridge GPS Nieznane (%s) Czujnik binarny Wysyłanie lokalizacji GPS do %1$d urządzeń @@ -1928,11 +1928,11 @@ Klasyczne Turbo Speed Odrzuć - Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Amazfit GTR3. -\n -\nUrządzenie uruchomi się ponownie po instalacji pliku .zip. -\n -\nWykonujesz to na własne ryzyko! + Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Twoim Amazfit GTR3. +\n +\nUrządzenie uruchomi się ponownie po instalacji pliku .zip. +\n +\nKONTYNUUJ NA WŁASNE RYZYKO! PozwÃŗl aplikacjom firm trzecich na zmianę ustawień Taniec Ćwiczenia w domu @@ -1979,7 +1979,7 @@ \n \nOpaska uruchomi się ponownie po instalacji *.zip. \n -\nKontynuujesz na własne ryzyko! +\nKONTYNUUJ NA WŁASNE RYZYKO! Lista ignorowanych powiadomień ze stron społecznościowych Przetwarzaj powiadomienia multimedialne przed listą aplikacji. Jeśli ta preferencja nie jest zaznaczona, aplikacje multimedialne muszą być dozwolone na liście aplikacji, aby elementy sterujące multimediami działały na urządzeniu. Automatyczne wykrywanie kategorii ćwiczeń @@ -2079,7 +2079,7 @@ \n \nOpaska uruchomi się ponownie po instalacji *.zip. \n -\nKontynuujesz na własne ryzyko! +\nKONTYNUUJ NA WŁASNE RYZYKO! DÅēwięki i wibracje Interfejs API intencji Tekst na mowę @@ -2105,12 +2105,12 @@ \n \nOpaska uruchomi się ponownie po instalacji *.zip. \n -\nKontynuujesz na własne ryzyko! +\nKONTYNUUJ NA WŁASNE RYZYKO! Zamierzasz zainstalować oprogramowanie układowe %s na Twoim Amazfit GTR 4. \n \nOpaska uruchomi się ponownie po instalacji *.zip. \n -\nKontynuujesz na własne ryzyko! +\nKONTYNUUJ NA WŁASNE RYZYKO! Czas powiadamiania o wygasaniu AGPS Kierunek ćwiczeń UmoÅŧliwi to dostęp do wszystkich dostępnych ustawień, nawet jeśli nie są obsługiwane przez urządzenie. MoÅŧe to spowodować niestabilność i awarie urządzenia. @@ -2121,7 +2121,7 @@ \n \nOpaska uruchomi się ponownie po instalacji *.zip. \n -\nKontynuujesz na własne ryzyko! +\nKONTYNUUJ NA WŁASNE RYZYKO! Mieszanie GPS Wyszukiwanie satelit Wibracja koronki @@ -2129,7 +2129,7 @@ Przykryj, aby wyciszyć Automatyczny kierunek ćwiczeń Lista Do zrobienia - Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Amazfit GTR 3 Pro. + Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Twoim Amazfit GTR 3 Pro. \n \nUrządzenie uruchomi się ponownie po instalacji pliku .zip. \n @@ -2147,7 +2147,7 @@ Zreleksowany Ślad GPX Ustawienia Mi Band 1/2 - chorwacki + Chorwacki Zatrzymaj Casio GMW-B5000 Długie naciśnięcie @@ -2203,4 +2203,586 @@ Amazfit Cheetah (Square) Amazfit Cheetah (Round) EKSPERYMENTALNE + UÅŧyj danych GPS telefonu, aby nadpisać dane GPS urządzenia bangle + Włącz, jeśli kontrola nośnikÃŗw urządzenia nie działa dla niektÃŗrych aplikacji + Aby odbierać połączenia Bluetooth, naleÅŧy sparować telefon z drugą instancją zegarka. + Naciśnij tu, aby rozpocząć proces parowania + Wyświetl numer telefonu lub imię dla połączeń przychodzących + 1. Naciśnij poniÅŧszy przycisk, aby rozpocząć proces parowania. + Korzystanie z danych GPS telefonu + OSTRZEÅģENIE: Jeśli włączysz połączenia Bluetooth bez parowania z drugą instancją, powiadomienia o połączeniach mogą nie działać zgodnie z oczekiwaniami. + Dynamiczne kolory + 3. Włącz poniÅŧsze ustawienie połączeń Bluetooth. + Paruj dla połączeń bluetooth + Bezpośrednie nadawanie intencji przyciskÃŗw multimedialnych + Częstotliwość aktualizacji pozycji GPS, w ms + Właśnie zamierzasz zainstalować oprogramowanie układowe %s na swoim Amazfit Bip 3 Pro. +\n +\nProszę upewnić się, Åŧe zainstalowany został plik .fw, następnie plik .res i na końcu plik .gps. Po zainstalowaniu pliku .fw TwÃŗj zegarek zostanie ponownie uruchomiony. +\n +\nUwaga: Nie musisz instalować plikÃŗw .res i .gps, jeśli te pliki są dokładnie takie same jak te, ktÃŗre zostały wcześniej zainstalowane. +\n +\nKONTYNUUJ NA WŁASNE RYZYKO! + UÅŧywaj sieci tylko do określania lokalizacji + UmoÅŧliwia zegarkowi wyzwalanie aparatu telefonu + Ustawienia połączeń Bluetooth + Parowanie połączeń Bluetooth + Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Twoim Amazfit Cheetah Pro +\n +\nTwÃŗj zegarek zostanie ponownie uruchomiony po instalacji pliku .zip +\n +\nKONTYNUUJ NA WŁASNE RYZYKO! + Pamięć podręczna poza zasięgiem + Do określenia lokalizacji uÅŧywaj tylko dostawcy sieci. Zmniejsza to zuÅŧycie energii kosztem dokładności. Konieczne jest ponowne połączenie. + Interwał aktualizacji danych GPS + Właśnie zamierzasz zainstalować oprogramowanie układowe %s na Twoim %s. +\n +\nTwÃŗj zegarek zostanie ponownie uruchomiony po instalacji pliki .zip. +\n +\nKONTYNUUJ NA WŁASNE RYZYKO! + Wysyłanie nieodebranych powiadomień, gdy urządzenie połączy się ponownie po utracie zasięgu + 2. PrzejdÅē do ustawień bluetooth telefonu i sparuj z nowym urządzeniem, ktÃŗre się pojawi (nazwa podobna do obecnego zegarka, ale z przyrostkiem, np. \"Amazfit GTR 4 - AFC8\"). + Wiadomość + Jak odbierać połączenia bluetooth + Tryby przyciskÃŗw - Pomoc + Ustawienia połączeń przychodzących + Przesyłaj instrukcje z aplikacji nawigacyjnych na zegarek + Uruchom licznik aktywności + Uruchom pełną synchronizację wszystkich danych dotyczących aktywności + Na koniec ustaw rękę aktywności na 100%. NaleÅŧy pamiętać, Åŧe ta ręka porusza się wyłącznie zgodnie z ruchem wskazÃŗwek zegara. + Nazwa kontaktu jest pusta + Ikona Ustawienia jest zawsze wyświetlana na końcu + 3 razy + PokaÅŧ powiązane urządzenia towarzyszące + Pobieranie dziennikÃŗw debugowania + Numer kontaktowy jest pusty + Ikona stanu + Tytuł + Pobieranie danych SpO2 + Francuski (Kanada) + PodwÃŗjne dotknięcie + Blokada Qrio + Zatrzymaj rejestrowanie dziennikÃŗw aplikacji zegarka + Czas wyłączenia + KrÃŗtkie + Struktura menu: %s + Squash + Upłynął limit czasu potwierdzenia, Kontynuowanie + Na urządzeniu nie ma wystarczającej ilości wolnego miejsca + Pobieranie podsumowań sportowych + Kalorie + Zainstaluj Catimę + Do lewej + Bez LED + Hiszpański (Hiszpania) + Narciarstwo + Udostępnianie pliku nie powiodło się. + Kategorie porannych aktualizacji + BieÅŧące MTU %1$d jest zbyt niskie. Włącz wysokie MTU w ustawieniach urządzenia i odłącz/podłącz ponownie urządzenie. + Synchronizuj karty lojalnościowe + Trener Zepp + Zmniejszające się + Kontroluj serwer FTP na zegarku + PAI na tydzień + Inteligentne wibracje + Riiiver + Ukryj tylko głÃŗwną treść + PozwÃŗl urządzeniu Wena okresowo prosić Gadgetbridge o pobranie danych o aktywności z urządzenia + Lista kategorii wyświetlana kaÅŧdego ranka + Angielski (Indie) + RÃŗwnowaga Suica + Styl biznesowy + Średnie + Szmaragdowe światło księÅŧyca + OstrzeÅŧenie: te preferencje dotyczą tylko opasek Mi Band 1 i 2. + Ustawienia aktywności + Catima + Utrzymuj ekran włączony podczas treningu + PAI miesięcznie + Nieprawidłowa struktura menu JSON + Redukcja szumÃŗw ← → Otoczenie ← → Wył + Pobieranie danych o tętnie + Rozpocznij rejestrowanie dziennikÃŗw aplikacji zegarka + Ciągłe + Urządzenie nie posiada wolnych miejsc na styki (całkowita liczba miejsc: %1$s) + Ekran pozostanie włączony podczas treningu, a jasność zostanie dostosowana tak, aby stale wyświetlać dane treningu w czasie rzeczywistym + Liczba krokÃŗw + Czy na pewno chcesz usunąć \'%1$s\'\? + Połączenie nie zostało potwierdzone na zegarku, przy uÅŧyciu trybu nieuwierzytelnionego + Kontroluj hotspot Wi-Fi na zegarku + Qrio + Wibrujący + Mi Band HRX + Totalny trening + Pomijanie potwierdzenia na urządzeniu + ZNALAZŁEM + Włącz wykrywanie nowej aktywności, ktÃŗra powinna rozwiązać problemy z wykrywaniem urządzeń. Wyłącz tę opcję, jeśli napotkasz jakiekolwiek problemy podczas wyszukiwania lub parowania z urządzeniem. + Aby skanowanie działało prawidłowo, naleÅŧy przyznać i włączyć dostęp do skanowania Bluetooth + Snowboard + Pobieranie danych o obciąÅŧeniu + Włącz dzienniki aplikacji zegarka + Biały + Jazda konna + Obecny czas + Pakiet usług głosowych + Synchronizacja danych dotyczących aktywności w tle + Łagodny + Słabe + Opis + Ikony ekranu głÃŗwnego + Wyświetl dziennik zmian od ostatniej wersji po aktualizacji Gadgetbridge + Pobieranie danych dotyczących częstości oddechÃŗw podczas snu + Sparuj bieÅŧące urządzenie jako towarzysza + Wyświetlaj aktualizacje kaÅŧdego ranka + Karty + Pogoda w pasku stanu + Synchronizuj tylko określone grupy + Gadgetbridge potrzebuje uprawnień do odczytu kart Catima, aby je zsynchronizować. Dotknij tego przycisku, aby je przyznać. + Czerwona fantazja + Karty skrÃŗtÃŗw + Skala Celciusza + Zwiększające się + Do prawej + Konfiguracja kontaktÃŗw + Podstawowe + Szybka uwaga + Synchronizuj zarchiwizowane karty + Grupy do synchronizacji + Ustawienia wyświetlacza + Katalog głÃŗwny + Większy rozmiar czcionki + Przesyłanie trasy gpx + Parowanie z zegarkiem nie powiodło się + Angielski (Australia) + Energia ciała + %1$s umoÅŧliwia wysyłanie wiadomości i innych danych z Androida na Twoje urządzenie. Aby to zrobić, wymagany jest dostęp do tych danych i bez niego moÅŧe nie działać poprawnie. +\n +\nZostanie wyświetlonych kilka okien dialogowych Androida z prośbą o te uprawnienia. +\n +\nAby kontynuować, kliknij \'%2$s\'. + Nazwa pakietu Catima + Dwukrotnie + Urządzenie wyłączy się i włączy automatycznie według określonego harmonogramu + Maks VO₂ + Włącz wyświetlacz, patrząc na nadgarstek + Resetuj strukturę menu + Nazwa + Wyślij nawigację do zegarka + Ok + Opis kaÅŧdego trybu przycisku + Przeciągnij uchwyt + Zwiększ rozmiar czcionki w kalendarzu, powiadomieniach itp. + Kliknij, aby zsynchronizować karty z zegarkiem + PodwÃŗjne naciśnięcie + OtwÃŗrz Catimę + Szybkie + Wyślij SMS-a na urządzenie + Blokada Qrio + Prawdopodobnie nie zainstalowano aplikacji „HR Menu Companion” + Skala temperatury + Ostatni trening + Aplikacja zawierająca usługę obsługującą polecenia głosowe + Jednorazowo + Otrzymuj powiadomienia o połączeniach + Pełna ścieÅŧka usługi obsługującej polecenia głosowe + PokaÅŧ ikonę aktualnych warunkÃŗw w lewym gÃŗrnym rogu ekranu głÃŗwnego + Pobieranie szczegÃŗłÃŗw sportowych + Udostępnij surowe podsumowanie + Wena Pay + Przesyłanie trasy Gpx nie powiodło się + Uruchom serwer FTP na zegarku + Spowoduje to pełną synchronizację wszystkich danych dotyczących aktywności z urządzenia. Ukończenie moÅŧe zająć kilka minut. + Dzienny wzrost + Usługa głosowa + Akcja przycisku + Czas wznowienia działania + Nowości + Dzienniki aplikacji + Teraz uÅŧyj pokrętła, aby ustawić wskazÃŗwkę minutową na godzinie 12. + Brak uprawnień + Karty lojalnościowe + Włącz wykrywanie nowej aktywności + Wibracja połączenia przychodzącego + Baseball + Zielony + Przesyłanie trasy Gpx zostało zakończone + Superpozycja + ÅģÃŗłty + Sony Wena 3 + Kolor diody LED połączenia przychodzącego + Pełna synchronizacja + Synchronizowanie %d kart lojalnościowych z urządzeniem + Francuski (Francja) + Zatrzymaj hotspot Wi-Fi na zegarku + Zezwalaj na polecenia debugowania + Nie moÅŧna zainstalować pliku, urządzenie nie jest obsługiwane. + Uwaga: aby uzyskać motyw dynamicznych kolorÃŗw, musisz włączyć opcję Kolory tapety lub Paleta kolorÃŗw w ustawieniach wyglądu Androida 12+. Jeśli tego nie zrobisz, Gadgetbridge uÅŧyje domyślnych kolorÃŗw Material 3. + Proste dane + Czerwony + Klasa usług głosowych + Jasnoniebieski + Uruchom hotspot Wi-Fi na zegarku + Syrena + Wybierz, czy urządzenie ma uÅŧywać skali Celsjusza czy Fahrenheita. + Ikony Menu + Dalej + Hiszpański (Meksyk) + Synchronizuj tylko karty oznaczone gwiazdką + Lista zmian + Skonfiguruj kontakty na zegarku + Nie udało się otworzyć sklepu z aplikacjami, aby zainstalować Catimę + Miniaturka + Synchronizuj + Angielski (Wielka Brytania) + PowtÃŗrzenie wibracji połączenia przychodzącego + Zezwalaj na uruchamianie poleceń menu debugowania za pośrednictwem interfejsu Intent API + Opcje synchronizacji + Wibracja powiadomień + Zainstalowana wersja Catimy nie jest kompatybilna z Gadgetbridge. Zaktualizuj Catima i Gadgetbridge do najnowszych wersji. + W sposÃŗb nieokreślony + Udostępnij surowe szczegÃŗły + Windsurfing + 4 razy + Piłka ręczna + Skala Fahrenheita + Siła wibracji + Angielski (Stany Zjednoczone) + Dynamiczne kolory nie są dostępne na Twoim urządzeniu, tylko Android 12+ obsługuje tę funkcję. Gadgetbridge uÅŧyje domyślnych kolorÃŗw Material 3. + Wstecz + Zatrzymaj serwer FTP na zegarku + Więcejâ€Ļ + Hiszpański (Stany Zjednoczone) + Jeśli ta opcja jest wyłączona, nie będziesz otrzymywać powiadomień o połączeniach przychodzących na urządzeniu Wena + Wiele danych + Długie naciśnięcie + Płatności + Łotewski + Strona stanu zamÃŗwień + Pojedyncze dotknięcie + Ustawienia alarmÃŗw + Struktura menu usunięta + PowtÃŗrzenie wibracji powiadomienia + W środku + Obracająca się Ziemia + Kitesurfing + Dodaje zaokrąglone prostokąty wokÃŗł ikon ekranu głÃŗwnego + Przebuduj tarczę zegarka, aby uzyskać niestandardowe menu + Withings Steel HR + Całkowite + Minimalistyczny + Catima jest potrzebna do zarządzania kartami lojalnościowymi + Kolor diody powiadomień + Wyłącz automatycznie redukcję szumÃŗw, gdy zaczniesz mÃŗwić. + Automatyczny wyłącznik + RÃŗwnowaga Edy + OstrzeÅŧenie + Margines inteligentnego alarmu + MI AI + Numer telefonu + Parowanie z zegarkiem powiodło się + UÅŧyj bogatego projektu + Angielski (Kanada) + Dzień zaczyna się o + Aby skanowanie działało prawidłowo, naleÅŧy przyznać i włączyć dostęp do połączenia Bluetooth + Karty skrÃŗtÃŗw widoczne po przesunięciu palcem w prawo na tarczy zegarka. To ustawienie nie ma wpływu na automatycznie generowane karty, gdy aplikacja jest uruchomiona. + Pośpiech + Struktura menu JSON ustawiona w GB + Pobieranie danych PAI + Ustawienia powiadomień dla poszczegÃŗlnych aplikacji + UÅŧyj poniÅŧszego pokrętła, aby ustawić wskazÃŗwkę godzinową na godzinie 12. + Brak wibracji + Fioletowy + Mocne + PotrÃŗjne + Niebieski + Ekran aktywności + Ustawienia powiadomień + OtwÃŗrz menu towarzyszące aplikacji + Sony WF-1000XM5 + Ciągłe naciskanie + PotrÃŗjne dotknięcie + Ignoruj powiadomienia z profilu słuÅŧbowego + Nie wysyłaj powiadomień z profilu słuÅŧbowego do zegarka + Znaki wspÃŗlne + Czysty biały + Ignoruj (cisza) + Średnia częstotliwość uderzeń + Ostateczna + Miejska prędkość + Słuchawki + tryb normalny (60~90s) + Odrzuć + Dowolna kombinacja + Skład ciała + Zepp Pay + Metoda odrzucenia połączenia + Pobieram statystyki + Tryb szybki (30s) + GwiaÅēdziste niebo + Amazfit Balance + Nawigacja została uruchomiona, ale na zegarku nie zainstalowano aplikacji do nawigacji. Zainstaluj ją z MenedÅŧera aplikacji. + jard + uderzeń/min + Tryb pomiaru + Działanie jakie jest podejmowane, gdy połączenie przychodzące zostanie odrzucone przez zegarek + Pobieram dane o temperaturze + Tryb dokładny (3min) + Ogromne Niebo + SkrÃŗty treningowe + Maksymalna częstotliwość uderzeń + Błyskawica + Długość pasa + Suma uderzeń + SkrÃŗty do aplikacji + Przewodnik + Gotowość + Termometr + Femometer Vinca II + Aplikacja do nawigacji nie jest zainstalowana na zegarku + Amazfit Active Edge + Średnia zawartość tlenu we krwi + Amazfit Active + Aplikacje nawigacyjne + Preferencje nawigacji + Mapy Google + Tytuł powiadomienia poprzedÅē nazwą aplikacji ÅērÃŗdłowej + SłuÅŧy do wyboru wersji OsmAnd, z ktÃŗrą chcesz się połączyć + Nazwa aplikacji w powiadomieniu + Nazwa pakietu OsmAnd + OsmAnd(+) + Alert dla pozostałych powiadomień + Alert powiadomień z kalendarza + Mi Watch Color Sport + Alert (wibracja/beep) dla połączeń przychodzących + Xiaomi Smart Band 8 + Bieganie + Harmonogram snu + Alert dla połączeń przychodzących + Pora snu + Alert powiadomień email + Alert (wibracja/beep) dla powiadomień email + Skupienie + Alert (wibracja/beep) dla powiadomień SMS (wiadomości tekstowych) + PokaÅŧ podgląd wiadomości w tytule + Redmi Watch 3 Active + Alert (wibracja/beep) dla powiadomień z kalendarza + Numer seryjny + Duński + Pobudka + Xiaomi Smart Band 7 Pro + Statystyki + Alert (wibracja/beep) dla pozostałych powiadomień + Xiaomi Watch Lite + PokaÅŧ podgląd wiadomości w tytule powiadomienia, jeÅŧeli udzielone jest odpowiednie uprawnienie + Wyślij przypomnienie i przed snem wejdÅē w tryb uśpienia. O zaplanowanej godzinie pobudki zabrzmi alarm. + Alert powiadomień SMS + Alerty + Xiaomi Watch S1 Active + Pixoo + Drugi punkt + Wysyłaj powiadomienia do urządzenia + Opaska (na nadgarstek) + Wysyłaj powiadomienia + Bieg szlakiem + Układ widÅŧetu + Urządzenie nie ma wolnych miejsc na ekrany widÅŧetÃŗw (łączna liczba miejsc: %1$s) + Nie przeszkadzać – Włączone + Tryb cichy telefonu + Nieznany trening - %s + Tryb noszenia + Pebble (klamra do butÃŗw) + Przesuń w gÃŗrę + Czy na pewno chcesz usunąć \'%1$s\'? + Klaśnij dłońmi, aby wybudzić ekran" + Normalny / Wibracje + Otrzymuj powiadomienie, gdy TwÃŗj wynik witalności osiągnie 30, 60 lub 100 w ciągu ostatnich 7 dni + Trekking + Wynik witalności + 2 widÅŧety + Usuń ekran widÅŧetu + Nie przeszkadzać — tylko alarmy + Ponownie klaśnięcie spowoduje wygaszenie ekranu" + 1 gÃŗra, 2 dÃŗł + ColaCao 2023 + 2 gÃŗra, 2 dÃŗł + Czujnik temperatury i wilgotności Mijia 2 + Musi być co najmniej %1$s ekranÃŗw + Wibracje / Cichy + WidÅŧet + Otrzymuj powiadomienie, gdy osiągniesz maksymalną liczbę punktÃŗw witalności na dany dzień + Redmi Smart Band 2 + Ekran widÅŧetu + Przesuń w dÃŗł + 1 widÅŧet + Nie przeszkadzać – tylko priorytetowe + Nie ustawiono + Podtyp widÅŧetu + Czas stania + Czas aktywności + Ekran %s + Wybierz wszystkie widÅŧety + 2 gÃŗra, 1 dÃŗł + ColaCao 2021 + Necklace (pasek na szyję) + Ekran wyłączy się, gdy mikrofon wykryje przez chwilę ciszę + Dzienny postęp + 7-dniowy postęp + Nie przeszkadzać – wyłączone + Normalny / Cichy + Zapasy + Instrukcje nawigacji + Konfiguracja zachowania aplikacji do nawigacji w zegarku + Określ, czy aplikacja nawigacyjna powinna automatycznie wyświetlać się na pierwszym planie po otrzymaniu instrukcji nawigacyjnych + Wibruj przy nowej instrukcji + PrzejdÅē do pracy na pierwszym planie + Nazwa urządzenia + Wibracje zegarka przy kaÅŧdej nowej lub zmienionej instrukcji nawigacji (tylko gdy aplikacja jest na pierwszym planie) + Redmi Watch 2 Lite + Stan połączenia + Redmi Smart Band Pro + Nothing Ear (2) + Nothing Ear (Stick) + Lekka aktywna redukcja szumÃŗw + Przezroczyste + Huawei Talk Band B6 + Huawei Watch GT 2e + Honor Band 4 + Honor Band 5 + Honor Band 7 + Huawei Watch GT 2 (Pro) + Huawei Band 4 (Pro) + Huawei Watch GT + Huawei Band 8 + Huawei Band 7 + Wyślij Åŧądanie odpluskwienia do urządzenia Huawei + Åģądanie odpluskwienia + HUAWEI TruSleep â„ĸ + Huawei Watch GT 3 (Pro) + Huawei Band 6 + Honor Band 3 + Honor Band 6 + Huawei Band (AW70) + Włącz automatyczne mierzenie SpO2 + Ręcznie + Połącz się ponownie tylko z połączonymi urządzeniami + Połącz się ponownie tylko z połączonymi urządzeniami, zamiast ponownego łączenia z wszystkimi urządzeniami + Ta opcja będzie działać dopiero w nowszej wersji aplikacji + Poziom baterii urządzenia jest zbyt niski + Cały dzień + NiektÃŗre urządzenia fałszywie twierdzą, Åŧe nie mają wsparcia dla pewnych funkcji. Tej opcji moÅŧna uÅŧyć by włączyć je pomimo tego. +\nKONTYNUUJ NA WŁASNE RYZYKO! +\nPrzeczytaj wiki + Powiadomienie na urządzeniu, po rozłączeniu Bluetooth. + Wymuś opcje + Nie przeszkadzać, gdy nie noszone + Tryb pracy + Nie odznaczaj inteligentnego wybudzania. + Nie zaznaczaj inteligentnego wybudzania. + Monitoruj jakość snu i sposÃŗb oddychania w czasie rzeczywistym. +\nPrzeanalizuj swoje wzorce snu i dokładnie zdiagnozuj 6 rodzajÃŗw problemÃŗw ze snem. + Wymuś inteligentny alarm + Ignoruj stan rozpoczęcia wybudzania + MoÅŧe pomÃŗc w prawidłowym wykrywaniu snu. Widoczne natychmiast w widoku codziennych aktywności. + Ignoruj stan zakończenia wybudzania + MoÅŧe pomÃŗc w prawidłowym wykrywaniu snu. Widoczne natychmiast w widoku codziennych aktywności. + Przeanalizuj ponownie dane treningu + Wymuś obsługę inteligentnych alarmÃŗw. +\nUÅģYWAJ NA WŁASNE RYZYKO + Wymuś lokalizację zuÅŧycia + Wymuszone wsparcie lokalizacji zuÅŧycia. +\nUÅģYWAJ NA WŁASNE RYZYKO + Intensywność brzęczyka + Jedynie gdy jest włączona opcja: podnieś, aby wybudzić ekran + Włącz odbieranie połączeń z poziomu urządzenia + Włącz odbieranie połączeń + Włącz odrzucanie połączeń + Włącz odrzucanie połączeń z poziomu urządzenia + Włącz automatyczny pomiar tętna + Wylącz funkcję ZnajdÅē mÃŗj telefon, gdy tryb Nie przeszkadzać jest włączony + Wymuś tryb Nie Przeszkadzać. +\nKONTYNUUJ NA WŁASNE RYZYKO! + Wymuś wspacie dla trybu Nie przeszkadzać + Poprawione monitorowanie snu + Hasło musi składać się z 4 znakÃŗw i zawierać jedynie cyfry + Płaski dystans + Wersja 1 + Wersja 2 + Wersja 3 + Xiaomi Watch S1 + Wersja protokołu + Xiaomi Watch S3 + Xiaomi Watch S1 Pro + Instalacja tarczy zegarka nie powiodła się + Instalacja tarczy zegarka zakończona + Wysyłanie tarczy zegarka + Wysyłanie tarczy zegarkaâ€Ļ + Wymuś typ połączenia + Xiaomi Smart Band 8 Pro + Mijia MHO-C303 + Info o aktywności + MoÅŧesz sprÃŗbować wymusić typ połączenia na wypadek, gdyby urządzenie nie odpowiedziało na Gadgetbridge + Automatycznie + Bluetooth LE + Bluetooth Classic + 155 uderzeń/min + 165 uderzeń/min + 175 uderzeń/min + 185 uderzeń/min + 195 uderzeń/min + 205 uderzeń/min + Orbitrek + Ergometr wioślarski + Crossfit + Trening fizyczny + Karate + Gra fitness + Aerobik + Pompki + Podciąganie się + Plank + Trampolina + Taniec jazzowy + Taniec latynoski + Balet + Inny taniec + Jazda na rolkach + Sztuki walki + Tai chi + Hula hop + Sporty z dyskiem + Rzutki + Łucznictwo + Jazda konno + Puszczanie latawca + Schody + Wędkarstwo + DrąÅŧek + Poręcze symetryczne + Rozciąganie + Kabaddi + Karting + Bilard + Futbol australijski + Pickleball + Åģeglarstwo + Hokej na lodzie + Sporty zimowe + Jazda na deskorolce + Wspinaczka skałkowa + Dwa ognie + Sony WI-SP600N + Honor MagicWatch 2 + CMF Watch Pro + Spacer na zewnątrz + Wyłącz przypomnienia o nawadnianiu dla zadanego przedziału czasu + PrÃŗg alarmowy tętna podczas intensywnej aktywności + Bieg wewnątrz + WędrÃŗwka gÃŗrska + Trening fitness + Taekwondo + Szermierka + Kendo + Polowanie + Przysiady + Softball \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 892dbe47e..5e9264326 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -753,6 +753,7 @@ Visível apenas se nenhum dispositivo estiver adicionado %d hora + %d horas %d horas Para visualizar o rastreamento de atividade, instale um aplicativo que consegue manipular arquivos GPX. @@ -1504,4 +1505,18 @@ Excluir \'%1$s\' Tem certeza que deseja excluir o relÃŗgio mundial\? Etiqueta + PrecisÃŖo + Equilibrado + Aplicativo companheiro Android para Bangle.js construído em cima do projeto Gadgetbridge, com acesso adicional à Internet. +\n +\nDevido às políticas Google Play Store, nÃŖo somos permitidos ter um link de doaçÃŖo no prÃŗprio aplicativo, mas se vocÃĒ gosta deste aplicativo, por favor, considere doar atravÊs da pÃĄgina inicial do Gadgetbridge abaixo. + Banda Ãēnica + Sobre o Bangle.js Gadgetbridge (Nightly) + Economia de energia + Nightly Bangle.js em execuçÃŖo + Obter mediçÃŖo da frequÃĒncia cardíaca + Bangle.js Gadgetbridge (Nightly) + Registro de mudanças + Personalizado + Bangle.js Gadgetbridge (Nightly) \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index ef752c56f..ffa7f4d93 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -556,6 +556,7 @@ nodomain.freeyourgadget.gadgetbridge.ButtonPressed %d hora + %d horas %d horas AtençÃŖo! Pressionando este botÃŖo, irÃĄ limpar o seu banco de dados e começar do zero. @@ -1474,7 +1475,7 @@ perguntar automÃĄtico nenhum - Tempo de actualizaçÃŖo em minutos: + Tempo de actualizaçÃŖo em minutos Mostrar informaçÃĩes de atividade no cartÃŖo do dispositivo Mostrar passos, distÃĸncia ou sono atual no cartÃŖo do dispositivo Sono @@ -1621,4 +1622,16 @@ 5 segundos 10 segundos Estresse + PrecisÃŖo + Equilibrado + GPS + Poupança de energia + CalendÃĄrio + Todos os satÊlites + Medir batimento cardíaco + Selecionar ficheiro + GPS + GNOLASS + GPS + BDS + Personalizado + GPS + GALILEO \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index b3fbb785a..91327b80a 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -2,20 +2,20 @@ Gadgetbridge Gadgetbridge - Setari - Depaneaza - Iesire - Doneaza - Sincronizeaza - Găsește brățara - Conecteaza - Deconecteaza - Sterge bratara - Sterge %1$s - Va fi stearsa bratara si datele asociate! - Deschide meniul de navigatie - Inchide meniul de navigatie - Apasati lung pentru a deconecta + Setări + Depanează + Ieșire + Donează + Sincronizează + Găsește dispozitivul pierdut + Se conecteazăâ€Ļ + Deconectează + Șterge dispozitiv + Șterge %1$s + Această opțiune va șterge dispozitivul și toate datele asociate cu acesta! + Deschide meniul de navigație + Închide meniul de navigație + Apăsați lung pe card pentru a deconecta Manager aplicatii Reinstaleaza Activeaza @@ -56,10 +56,10 @@ Afiseaza numele, ascunde numarul Ascunde numele si numarul Meteo - Captura ecran - Se deconecteaza - Conectareâ€Ļ - Capturez ecranul dispozitivului + Captură ecran + Se deconectează + Se conecteazăâ€Ļ + Fă o captură de ecran Depanare Aplicatii instalate Fete de ceas instalate @@ -107,8 +107,8 @@ Compas Setari Alipay - Modificare Culoare LED - Modificare Frecvență FM + Modifică culoarea LED-ului + Modifică frecvența FM Calibrare Dispozitiv Aplicație Redare Audio Preferată Ascunde notificarea Gadgetbridge @@ -134,4 +134,64 @@ Modul de economisire a energiei dezactivează măsurarea automată periodică a ritmului cardiac, crescÃĸnd astfel timpul de lucru Intervalul de alarmă inteligentă este intervalul ÃŽnainte de alarma instalată. În acest interval, dispozitivul ÃŽncearcă să detecteze cea mai ușoară fază de somn pentru a trezi utilizatorul Vibrație scăzută activată + Distanța este calculată din numărul de pași și valoarea lungimii pașilor (reglabile ÃŽn Setări - Despre tine) + Gadgetbridge (Nightly, fără funizor Pebble) + Oprire + Acuratețe + Gadgetbridge Nightly + Două brățări + Despre Bangle.js Gadgetbridge + 3 luni + Gadgetbridge Nightly fără Pebble + Bangle.js Gadgetbridge + Echilibrat + Somn + Setează poreclă + Toți sateliții + Acuratețea are prioritate + Despre Gadgetbridge Nightly fără Pebble + SăptămÃĸnă + GPS + 6 luni + Ești sigur că dorești să oprești acest dispozitiv ? + Zi + Informații despre activitate pe cardul dispozitivului + Despre Gadgetbridge Nightly + O singură brățară + Nightly NoPebble GB running + Două săptămÃĸni + Despre Bangle.js Gadgetbridge (Nightly) + An + Economisirea energiei + Caută %1$s? + Nightly Bangle.js rulează + Obține valorile ritmului cardiac + Bangle.js rulează + Arată numărul total de pași + Bangle.js Gadgetbridge (Nightly) + Arată durata somnului + Nivelul bateriei + Bangle.js Gadgetbridge + Lună + Arată informații despre activitate pe cardul dispozitivului + Informații despre baterie + Elimini preferințele dispozitivului? + Alege ce detalii vor fii afișate pe cardul dispozitivului + Înlocuitor sub o licență copyleft pentru aplicațiile cu sursă ÃŽnchisă Android, de administrare a unor dispozitive portabile. + GPS + GNOLASS + Nightly GB este pornit + Jurnal de modificări + Despre Gadgetbridge + Gadgetbridge este pornit + Oprire + GPS + BDS + GPS cu consum mic + Personalizat + Viteza are prioritate + Afișează numărul de pași, distanța sau somnul pe cardul dispozitivului + Bangle.js Gadgetbridge (Nightly) + Gadgetbridge (Nightly) + GPS + GALILEO + Chiar dorești o restaurare a setărilor din fabrică? + Xiaomi Watch S1 Active \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e1f744847..7a3d6ab8d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -50,7 +50,7 @@ НаŅŅ‚Ņ€ĐžĐšĐēи ОбŅ‰Đ¸Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи - ПодĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Đē дОйавĐģĐĩĐŊĐŊĐžĐŧŅƒ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вŅƒ ĐŋŅ€Đ¸ вĐēĐģŅŽŅ‡ĐĩĐŊии Bluetooth + ПодĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Đē дОйавĐģĐĩĐŊĐŊŅ‹Đŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ваĐŧ ĐŋŅ€Đ¸ вĐēĐģŅŽŅ‡ĐĩĐŊии Bluetooth ЗаĐŋŅƒŅĐēĐ°Ņ‚ŅŒ авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи ПĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи ПŅ€ĐĩĐ´ĐŋĐžŅ‡Ņ‚иŅ‚ĐĩĐģŅŒĐŊŅ‹Đš ĐŧŅƒĐˇŅ‹ĐēĐ°ĐģŅŒĐŊŅ‹Đš ĐŋĐģĐĩĐĩŅ€ @@ -302,7 +302,7 @@ НĐĩŅĐžĐ˛ĐŧĐĩŅŅ‚иĐŧĐ°Ņ ĐŋŅ€ĐžŅˆĐ¸Đ˛ĐēĐ° Đ­Ņ‚Đ° ĐŋŅ€ĐžŅˆĐ¸Đ˛ĐēĐ° ĐŊĐĩ ŅĐžĐ˛ĐŧĐĩŅŅ‚иĐŧĐ° Ņ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вОĐŧ Đ ĐĩСĐĩŅ€Đ˛ĐŊŅ‹Đĩ ŅĐ¸ĐŗĐŊĐ°ĐģŅ‹ Đ´ĐģŅ ĐŋŅ€ĐĩĐ´ŅŅ‚ĐžŅŅ‰Đ¸Ņ… ŅĐžĐąŅ‹Ņ‚иК - ИŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņ‡Đ¸Đē ŅĐĩŅ€Đ´Ņ†ĐĩйиĐĩĐŊиŅ Đ´ĐģŅ ŅƒĐģŅƒŅ‡ŅˆĐĩĐŊиŅ ĐŧĐžĐŊиŅ‚ĐžŅ€Đ¸ĐŊĐŗĐ° ŅĐŊĐ° + ИŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ Đ´Đ°Ņ‚Ņ‡Đ¸Đē ŅĐĩŅ€Đ´Ņ†ĐĩйиĐĩĐŊиŅ Đ´ĐģŅ ŅƒĐģŅƒŅ‡ŅˆĐĩĐŊиŅ ĐžŅ‚ŅĐģĐĩĐļиваĐŊиŅ ŅĐŊĐ° ĐĄĐŧĐĩŅ‰ĐĩĐŊиĐĩ вŅ€ĐĩĐŧĐĩĐŊи в Ņ‡Đ°ŅĐ°Ņ… (Đ´ĐģŅ Ņ‚ĐĩŅ…, ĐēŅ‚Đž Ņ€Đ°ĐąĐžŅ‚Đ°ĐĩŅ‚ ĐŋĐž ĐŊĐžŅ‡Đ°Đŧ) ФОŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚Ņ‹ ВŅ€ĐĩĐŧŅ @@ -315,8 +315,8 @@ ПоĐģ Đ ĐžŅŅ‚ в ŅĐŧ ВĐĩŅ в ĐēĐŗ - АвŅ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ - ĐĸŅ€ĐĩĐąŅƒĐĩŅ‚ŅŅ авŅ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ + АŅƒŅ‚ĐĩĐŊŅ‚иŅ„иĐēĐ°Ņ†Đ¸Ņ + ĐĸŅ€ĐĩĐąŅƒĐĩŅ‚ŅŅ Đ°ŅƒŅ‚ĐĩĐŊŅ‚иŅ„иĐēĐ°Ņ†Đ¸Ņ ĐĨŅ€Ņ€ ДобавиŅ‚ŅŒ видĐļĐĩŅ‚ ЖĐĩĐģĐ°ĐĩĐŧĐ°Ņ ĐŋŅ€ĐžĐ´ĐžĐģĐļиŅ‚ĐĩĐģŅŒĐŊĐžŅŅ‚ŅŒ ŅĐŊĐ° @@ -433,7 +433,7 @@ ШаĐŗОв в ĐŧиĐŊŅƒŅ‚Ņƒ ЧаŅŅ‹ ПŅƒĐģŅŒŅ - ЗаŅ€ŅĐ´ йаŅ‚Đ°Ņ€ĐĩĐšĐēи + ЗаŅ€ŅĐ´ йаŅ‚Đ°Ņ€Đĩи ДĐĩĐšŅŅ‚виŅ ĐēĐŊĐžĐŋĐēи НаŅŅ‚Ņ€ĐžĐšŅ‚Đĩ Đ´ĐĩĐšŅŅ‚виŅ ĐŋŅ€Đ¸ ĐŊĐ°ĐļĐ°Ņ‚ии ĐŊĐ° ĐēĐŊĐžĐŋĐēŅƒ КоĐģ-вО ĐŊĐ°ĐļĐ°Ņ‚иК ĐŊĐ° ĐēĐŊĐžĐŋĐēŅƒ @@ -460,14 +460,14 @@ НаŅ‡Đ°ĐģŅŒĐŊĐžĐĩ вŅ€ĐĩĐŧŅ ВŅ€ĐĩĐŧŅ ĐžĐēĐžĐŊŅ‡Đ°ĐŊиŅ АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи - ĐŖĐŋŅ€ĐžŅ‰Ņ‘ĐŊĐŊŅ‹Đš ĐēиŅ‚Đ°ĐšŅĐēиК ŅĐˇŅ‹Đē - ĐĸŅ€Đ°Đ´Đ¸Ņ†Đ¸ĐžĐŊĐŊŅ‹Đš ĐēиŅ‚Đ°ĐšŅĐēиК ŅĐˇŅ‹Đē + КиŅ‚Đ°ĐšŅĐēиК ŅƒĐŋŅ€ĐžŅ‰Ņ‘ĐŊĐŊŅ‹Đš + КиŅ‚Đ°ĐšŅĐēиК Ņ‚Ņ€Đ°Đ´Đ¸Ņ†Đ¸ĐžĐŊĐŊŅ‹Đš АĐŊĐŗĐģиКŅĐēиК ŅĐˇŅ‹Đē ИСĐŧĐĩŅ€ĐĩĐŊиĐĩ ĐŋŅƒĐģŅŒŅĐ° в Ņ‚ĐĩŅ‡ĐĩĐŊиĐĩ Đ´ĐŊŅ Ņ€Đ°Đˇ в ĐŧиĐŊŅƒŅ‚Ņƒ - Ņ€Đ°Đˇ в 5 ĐŧиĐŊŅƒŅ‚ - Ņ€Đ°Đˇ в 10 ĐŧиĐŊŅƒŅ‚ - Ņ€Đ°Đˇ в ĐŋĐžĐģŅ‡Đ°ŅĐ° + ĐēĐ°ĐļĐ´Ņ‹Đĩ 5 ĐŧиĐŊŅƒŅ‚ + ĐēĐ°ĐļĐ´Ņ‹Đĩ 10 ĐŧиĐŊŅƒŅ‚ + ĐēĐ°ĐļĐ´Ņ‹Đĩ 30 ĐŧиĐŊŅƒŅ‚ Ņ€Đ°Đˇ в Ņ‡Đ°Ņ ПоĐŗОда ОŅˆĐ¸ĐąĐēĐ° ŅĐēŅĐŋĐžŅ€Ņ‚Đ° йаСŅ‹ Đ´Đ°ĐŊĐŊŅ‹Ņ…! ПоĐļĐ°ĐģŅƒĐšŅŅ‚Đ°, ĐŋŅ€ĐžĐ˛ĐĩŅ€ŅŒŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи. @@ -566,12 +566,12 @@ КаĐģийŅ€ĐžĐ˛ĐēĐ° Watch 9 РаСйĐģĐžĐēиŅ€ĐžĐ˛ĐēĐ° ŅĐēŅ€Đ°ĐŊĐ° ĐĄĐŧĐ°Ņ…ĐŊиŅ‚Đĩ ввĐĩŅ€Ņ… Đ´ĐģŅ Ņ€Đ°ĐˇĐąĐģĐžĐēиŅ€ĐžĐ˛Đēи ŅĐēŅ€Đ°ĐŊĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚Đ° - НĐĩĐŧĐĩŅ†ĐēиК ŅĐˇŅ‹Đē - ИŅ‚Đ°ĐģŅŒŅĐŊŅĐēиК ŅĐˇŅ‹Đē - ФŅ€Đ°ĐŊŅ†ŅƒĐˇŅĐēиК ŅĐˇŅ‹Đē - ПоĐģŅŒŅĐēиК ŅĐˇŅ‹Đē - КоŅ€ĐĩĐšŅĐēиК ŅĐˇŅ‹Đē - Đ¯ĐŋĐžĐŊŅĐēиК ŅĐˇŅ‹Đē + НĐĩĐŧĐĩŅ†ĐēиК + ИŅ‚Đ°ĐģŅŒŅĐŊŅĐēиК + ФŅ€Đ°ĐŊŅ†ŅƒĐˇŅĐēиК + ПоĐģŅŒŅĐēиК + КоŅ€ĐĩĐšŅĐēиК + Đ¯ĐŋĐžĐŊŅĐēиК НоŅ€Đ˛ĐĩĐļŅĐēиК (ĐģиŅ‚ĐĩŅ€Đ°Ņ‚ŅƒŅ€ĐŊŅ‹Đš) ПодĐĩĐģиŅ‚ŅŒŅŅ ĐģĐžĐŗ-Ņ„Đ°ĐšĐģĐžĐŧ ФаКĐģŅ‹ ĐļŅƒŅ€ĐŊĐ°ĐģĐ° (log-Ņ„Đ°ĐšĐģŅ‹) Gadgetbridge ĐŧĐžĐŗŅƒŅ‚ ŅĐžĐ´ĐĩŅ€ĐļĐ°Ņ‚ŅŒ Ņ€Đ°ĐˇĐģиŅ‡ĐŊŅƒŅŽ ĐģиŅ‡ĐŊŅƒŅŽ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŽ, Ņ‚Đ°ĐēŅƒŅŽ ĐēĐ°Đē ŅƒĐŊиĐēĐ°ĐģŅŒĐŊŅ‹Đĩ идĐĩĐŊŅ‚иŅ„иĐēĐ°Ņ‚ĐžŅ€Ņ‹ (ĐŊĐ°ĐŋŅ€Đ¸ĐŧĐĩŅ€, MAC-Đ°Đ´Ņ€ĐĩŅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва), иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŽ Đž ĐŧŅƒĐˇŅ‹ĐēĐ°ĐģŅŒĐŊŅ‹Ņ… ĐŋŅ€ĐĩĐ´ĐŋĐžŅ‡Ņ‚ĐĩĐŊиŅŅ… и Ņ‚.Đ´. Đ ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩŅ‚ŅŅ Ņ€ĐĩĐ´Đ°ĐēŅ‚иŅ€ĐžĐ˛Đ°ĐŊиĐĩ Ņ„Đ°ĐšĐģĐ° ĐļŅƒŅ€ĐŊĐ°ĐģĐ° и ŅƒĐ´Đ°ĐģĐĩĐŊиĐĩ ĐģиŅ‡ĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸ ĐŋĐĩŅ€ĐĩĐ´ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐēОК в ĐŋŅƒĐąĐģиŅ‡ĐŊĐžĐŧ ĐžŅ‚Ņ‡ĐĩŅ‚Đĩ Đž ĐŋŅ€ĐžĐąĐģĐĩĐŧĐĩ. @@ -707,14 +707,14 @@ \nВĐĢ ДЕЙСĐĸВĐŖЕĐĸЕ НА СВОЙ ĐĄĐĸРАĐĨ И РИСК! \n \nПОЛНОСĐĸĐŦĐŽ НЕ ПРОĐĸЕСĐĸИРОВАНО. ВОЗМОЖНО, НĐŖЖНО ПРОШИĐĸĐŦ ПРОШИВКĐŖ BEATS_W, ЕСЛИ Đ˜ĐœĐ¯ ВАШЕГО ĐŖĐĄĐĸРОЙСĐĸВА \"Amazfit Band 2\" - ГоĐģĐģĐ°ĐŊĐ´ŅĐēиК ŅĐˇŅ‹Đē - ĐĸŅƒŅ€ĐĩŅ†ĐēиК ŅĐˇŅ‹Đē - ĐŖĐēŅ€Đ°Đ¸ĐŊŅĐēиК ŅĐˇŅ‹Đē - АŅ€Đ°ĐąŅĐēиК ŅĐˇŅ‹Đē - ИĐŊĐ´ĐžĐŊĐĩСиКŅĐēиК ŅĐˇŅ‹Đē - ĐĸĐ°ĐšŅĐēиК ŅĐˇŅ‹Đē - ВŅŒĐĩŅ‚ĐŊĐ°ĐŧŅĐēиК ŅĐˇŅ‹Đē - ПоŅ€Ņ‚ŅƒĐŗĐ°ĐģŅŒŅĐēиК ŅĐˇŅ‹Đē + ГоĐģĐģĐ°ĐŊĐ´ŅĐēиК + ĐĸŅƒŅ€ĐĩŅ†ĐēиК + ĐŖĐēŅ€Đ°Đ¸ĐŊŅĐēиК + АŅ€Đ°ĐąŅĐēиК + ИĐŊĐ´ĐžĐŊĐĩСиКŅĐēиК + ĐĸĐ°ĐšŅĐēиК + ВŅŒĐĩŅ‚ĐŊĐ°ĐŧŅĐēиК + ПоŅ€Ņ‚ŅƒĐŗĐ°ĐģŅŒŅĐēиК Amazfit Cor 2 Mi Band 4 ВŅ‹ ŅĐžĐąĐ¸Ņ€Đ°ĐĩŅ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚ŅŒ ĐŋŅ€ĐžŅˆĐ¸Đ˛ĐēŅƒ %s ĐŊĐ° ваŅˆ Mi Band 4. @@ -815,12 +815,12 @@ ОŅˆĐ¸ĐąĐēĐ° ĐŋĐĩŅ€ĐĩСаĐŋиŅĐ¸ ĐēĐŊĐžĐŋĐžĐē ŅĐŧĐĩŅ‰ĐĩĐŊиĐĩ Ņ‡Đ°ŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅĐ° ĐŊĐ° иСĐŧĐĩĐŊĐĩĐŊиŅ ĐŧĐžĐŗŅƒŅ‚ СаĐŊŅŅ‚ŅŒ ĐŊĐĩŅĐēĐžĐģŅŒĐēĐž ŅĐĩĐēŅƒĐŊĐ´â€Ļ - ŅĐŧĐĩŅ‰ĐĩĐŊиĐĩ Ņ‡Đ°ŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅĐ° ĐŊĐ° + ŅĐŧĐĩŅ‰ĐĩĐŊиĐĩ вŅ€ĐĩĐŧĐĩĐŊи ĐŊĐ° ОŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŊОвОĐĩ BLE ŅĐēĐ°ĐŊиŅ€ĐžĐ˛Đ°ĐŊиĐĩ ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, ĐĩŅĐģи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО ĐŊĐĩ видĐŊĐž ĐēĐ°Đē Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐžĐĩ Bangle.js Y5 - КоŅ€ĐžŅ‚ĐēиК ŅĐžĐŊ + ОŅ‚ĐēĐģĐ°Đ´Ņ‹Đ˛Đ°ĐŊиĐĩ ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐēĐ° ДоŅŅ‚ŅƒĐŋ Đē ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиŅŽ Đ´ĐžĐģĐļĐĩĐŊ ĐąŅ‹Ņ‚ŅŒ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐĩĐŊ и вĐēĐģŅŽŅ‡ĐĩĐŊ Đ´ĐģŅ ĐēĐžŅ€Ņ€ĐĩĐēŅ‚ĐŊОК Ņ€Đ°ĐąĐžŅ‚Ņ‹ ĐŋОиŅĐēĐ° iTag РаСŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ вŅ‹ŅĐžĐēиК MTU @@ -1008,7 +1008,7 @@ ĐĸŅ€Đ°ĐŊŅĐģŅŅ†Đ¸ĐžĐŊĐŊĐžĐĩ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ ВозвŅ‹ŅˆĐĩĐŊĐŊĐžŅŅ‚ŅŒ ПĐģĐžŅĐēиĐĩ - swolfIndex + SWOLF ПŅ€Đ¸ĐŧĐĩĐŊиŅ‚ŅŒ Ņ„иĐģŅŒŅ‚Ņ€ ФиĐģŅŒŅ‚Ņ€ ĐĄĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ Ņ„иĐģŅŒŅ‚Ņ€ @@ -1309,12 +1309,12 @@ ЗвŅƒĐēОвОК ŅĐ¸ĐŗĐŊĐ°Đģ НаŅŅ‚Ņ€ĐžĐšĐēи Đ´ĐģŅ ĐēĐ°ĐļĐ´ĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ РаСŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ ĐžŅ‚ вŅ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Ņ… ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК - ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ + ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ вŅŅ‘ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, ĐĩŅĐģи ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО ĐŋĐĩŅ€ĐĩŅŅ‚Đ°ĐģĐž ĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ ĐŋĐžŅĐģĐĩ ОйĐŊОвĐģĐĩĐŊиŅ ĐŋŅ€ĐžŅˆĐ¸Đ˛Đēи ПŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐŊĐĩ Đ´ĐžĐģĐļĐŊĐž ĐąŅ‹Ņ‚ŅŒ вŅ‹ĐąŅ€Đ°ĐŊĐž Đ´ĐģŅ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи НаŅŅ‚Ņ€ĐžĐšĐēи ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК Đ ĐĩĐļиĐŧ ĐēĐžĐŊŅ„идĐĩĐŊŅ†Đ¸Đ°ĐģŅŒĐŊĐžŅŅ‚и ŅĐžĐžĐąŅ‰ĐĩĐŊиК - ОŅ‚ОйŅ€Đ°ĐļĐ°Ņ‚ŅŒ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ + ОŅ‚ОйŅ€Đ°ĐļĐ°Ņ‚ŅŒ вŅŅ‘ ŅĐžĐ´ĐĩŅ€ĐļиĐŧĐžĐĩ ĐĄĐŋиŅĐžĐē ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК ВŅ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ВŅ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ @@ -1481,7 +1481,7 @@ Đ­Ņ‚Đ° Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ ĐŧĐžĐļĐĩŅ‚ ĐŋŅ€Đ¸Đ˛ĐĩŅŅ‚и Đē ĐŋĐžĐģĐžĐŧĐēĐĩ ваŅˆĐĩĐŗĐž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва. ОдĐŊĐ°ĐēĐž Ņ‚Đ°ĐēĐžĐŗĐž ĐŊиĐēĐžĐŗĐ´Đ° ĐŊĐĩ ŅĐģŅƒŅ‡Đ°ĐģĐžŅŅŒ ĐŊи Ņ ОдĐŊиĐŧ иС Ņ€Đ°ĐˇŅ€Đ°ĐąĐžŅ‚Ņ‡Đ¸ĐēОв иС-Са ĐŋĐĩŅ€ĐĩĐŋŅ€ĐžŅˆĐ¸Đ˛Đēи, ĐŊĐž ĐŋĐžĐŧĐŊиŅ‚Đĩ, Ņ‡Ņ‚Đž вŅ‹ Đ´ĐĩĐģĐ°ĐĩŅ‚Đĩ ŅŅ‚Đž ĐŊĐ° ŅĐ˛ĐžĐš ŅŅ‚Ņ€Đ°Ņ… и Ņ€Đ¸ŅĐē. КаĐģĐĩĐŊĐ´Đ°Ņ€ŅŒ ВĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ŅĐēŅ€Đ°ĐŊĐ° ĐŋŅ€Đ¸ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊии - АвŅ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ + АŅƒŅ‚ĐĩĐŊŅ‚иŅ„иĐēĐ°Ņ†Đ¸Ņ ВĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ŅĐēŅ€Đ°ĐŊ Ņ‡Đ°ŅĐžĐ˛, ĐēĐžĐŗĐ´Đ° ĐŋĐžŅŅ‚ŅƒĐŋĐ°ĐĩŅ‚ ĐžĐŋОвĐĩŅ‰ĐĩĐŊиĐĩ ОŅ‚ĐēĐģĐžĐŊиŅ‚ŅŒ ЗаĐŋŅ€ĐĩŅ‚иŅ‚ŅŒ вŅ‹ĐąŅ€Đ°ĐŊĐŊŅ‹Đŧ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅĐŧ ĐŋĐžĐēаСŅ‹Đ˛Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ @@ -1533,7 +1533,7 @@ ЛĐĩвŅ‹Đš ĐŊĐ°ŅƒŅˆĐŊиĐē 2-ОК Ņ‡Đ°ŅĐžĐ˛ĐžĐš ĐŋĐžŅŅ ВĐĩŅ€ĐžŅŅ‚ĐŊĐžŅŅ‚ŅŒ Đ´ĐžĐļĐ´Ņ - НĐĩ ОйĐŊОвĐģŅŅ‚ŅŒ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŽ ĐŊĐ° ŅĐēŅ€Đ°ĐŊĐĩ, ĐēĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžŅŅ‚вО ĐŊĐĩ ĐŊĐ°Đ´ĐĩŅ‚Đž + НĐĩ ОйĐŊОвĐģŅŅ‚ŅŒ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŽ ĐŊĐ° ŅĐēŅ€Đ°ĐŊĐĩ, ĐēĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО ŅĐŊŅŅ‚Đž НоŅ€ĐŧĐ°ĐģŅŒĐŊŅ‹Đš ĐŖŅĐ¸ĐģĐĩĐŊиĐĩ йаŅĐžĐ˛ ВибŅ€Đ°Ņ†Đ¸Ņ ĐŋŅ€Đ¸ ĐŋĐžŅŅ‚ŅƒĐŋĐģĐĩĐŊии СвОĐŊĐēОв, ŅĐžĐžĐąŅ‰ĐĩĐŊиК, ĐžĐŋОвĐĩŅ‰ĐĩĐŊиК и ĐŋŅ€ĐžŅ‡ĐĩĐŗĐž @@ -1604,7 +1604,7 @@ ĐŖŅĐ¸ĐģĐĩĐŊиĐĩ ВЧ ДиĐŊĐ°ĐŧиŅ‡ĐŊŅ‹Đš ПодĐĩĐģиŅ‚ŅŒŅŅ - ВŅ‹ĐēĐģŅŽŅ‡ĐĩĐŊĐž + ВŅ‹ĐēĐģ. ОŅ‚ĐŋŅ€Đ°Đ˛ĐēĐ° GPS вО вŅ€ĐĩĐŧŅ Ņ‚Ņ€ĐĩĐŊиŅ€ĐžĐ˛Đēи ЗаĐŋŅƒŅĐē/ĐžŅŅ‚Đ°ĐŊОвĐēĐ° ĐžŅ‚ŅĐģĐĩĐļиваĐŊиŅ Ņ„иŅ‚ĐŊĐĩŅ-ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ ĐŊĐ° Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊĐĩ ĐŋŅ€Đ¸ СаĐŋŅƒŅ‰ĐĩĐŊĐŊОК GPS-Ņ‚Ņ€ĐĩĐŊиŅ€ĐžĐ˛ĐēĐĩ ĐŊĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚Đĩ ОĐŋОвĐĩŅ‰ĐĩĐŊиŅ Đž ĐŋŅ€ĐžŅŅ‚ĐžĐĩ @@ -1690,7 +1690,7 @@ ĐĄĐžĐģĐŊŅ†Đĩ и ЛŅƒĐŊĐ° ĐĸĐĩĐģĐĩŅ„ĐžĐŊ Đ­ĐēŅ€Đ°ĐŊ ĐąĐģĐžĐēиŅ€ĐžĐ˛Đēи - ПаŅ€ĐžĐģŅŒ Đ´ĐžĐģĐļĐĩĐŊ ŅĐžŅŅ‚ĐžŅŅ‚ŅŒ иС 6 Ņ†Đ¸Ņ„Ņ€, иŅĐŋĐžĐģŅŒĐˇŅƒŅ Ņ‚ĐžĐģŅŒĐēĐž Ņ†Đ¸Ņ„Ņ€Ņ‹ + ПаŅ€ĐžĐģŅŒ Đ´ĐžĐģĐļĐĩĐŊ ŅĐžŅŅ‚ĐžŅŅ‚ŅŒ иС 6 Ņ†Đ¸Ņ„Ņ€ БĐģĐžĐēиŅ€ĐžĐ˛ĐēĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚Đ° ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ ĐŋŅ€Đ¸ ŅĐŊŅŅ‚ии Ņ СаĐŋŅŅŅ‚ŅŒŅ ИĐŊŅ‚ĐĩĐŊŅĐ¸Đ˛ĐŊĐ°Ņ иĐŊŅ‚ĐĩŅ€Đ˛Đ°ĐģŅŒĐŊĐ°Ņ Ņ‚Ņ€ĐĩĐŊиŅ€ĐžĐ˛ĐēĐ° ПŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐēи @@ -1758,8 +1758,8 @@ ĐĄĐēĐ°Ņ‡Đ°Ņ‚ŅŒ в ĐēŅŅˆ ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŊĐĩĐŋОддĐĩŅ€ĐļиваĐĩĐŧŅ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи НаŅŅ‚Ņ€ĐžĐ¸Ņ‚ŅŒ ĐžŅ‚ŅĐģĐĩĐļиваĐŊиĐĩ ŅĐĩŅ€Đ´ĐĩŅ‡ĐŊĐžĐŗĐž Ņ€Đ¸Ņ‚ĐŧĐ° - ВŅĐĩĐŗĐ´Đ° ĐŊĐ° ŅĐēŅ€Đ°ĐŊĐĩ - ОŅŅ‚авĐģŅŅ‚ŅŒ ŅĐēŅ€Đ°ĐŊ Ņ‡Đ°ŅĐžĐ˛ ĐŋĐžŅŅ‚ĐžŅĐŊĐŊĐž вĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ‹Đŧ + Đ­ĐēŅ€Đ°ĐŊ вŅĐĩĐŗĐ´Đ° вĐēĐģŅŽŅ‡ĐĩĐŊ + ВŅĐĩĐŗĐ´Đ° Đ´ĐĩŅ€ĐļĐ°Ņ‚ŅŒ ŅĐēŅ€Đ°ĐŊ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва вĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ‹Đŧ ЧŅƒĐ˛ŅŅ‚виŅ‚ĐĩĐģŅŒĐŊĐžŅŅ‚ŅŒ ЗŅƒĐŧйа Amazfit GTS 4 @@ -1870,7 +1870,7 @@ Bangle.js Gadgetbridge (Nightly) О Bangle.js Gadgetbridge (Nightly) Nightly Bangle.js СаĐŋŅƒŅ‰ĐĩĐŊ - ПодĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ĐŋĐž Bluetooth classic + ПодĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ Ņ‡ĐĩŅ€ĐĩС ОйŅ‹Ņ‡ĐŊŅ‹Đš Bluetooth AsteroidOS Amazfit GTR 3 %d ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚в ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐž @@ -1886,7 +1886,7 @@ Wi-Fi ПоŅĐģĐĩĐ´ĐŊиК авŅ‚Đž-ŅĐēŅĐŋĐžŅ€Ņ‚: %1$s АвŅ‚Đž-ŅĐēŅĐŋĐžŅ€Ņ‚ ĐąŅ‹Đģ иСĐŊĐ°Ņ‡Đ°ĐģŅŒĐŊĐž СаĐŋĐģĐ°ĐŊиŅ€ĐžĐ˛Đ°ĐŊ ĐŊĐ° %1$s - МĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ ĐŊĐĩ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģĐĩĐŊĐž. Đ­Ņ‚Đž ĐŧĐžĐļĐĩŅ‚ ĐąŅ‹Ņ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧĐ° Ņ ОйĐŊОвĐģĐĩĐŊĐŊОК ŅĐ¸ŅŅ‚ĐĩĐŧОК Ņ€Đ°ĐˇŅ€ĐĩŅˆĐĩĐŊиК Android. ĐĄĐēĐžŅ€ĐĩĐĩ вŅĐĩĐŗĐž авŅ‚Đž-ŅĐēŅĐŋĐžŅ€Ņ‚ ŅĐĩĐšŅ‡Đ°Ņ ĐŊĐĩ Ņ€Đ°ĐąĐžŅ‚Đ°ĐĩŅ‚. + НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģиŅ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ. ВозĐŧĐžĐļĐŊĐž, ŅŅ‚Đ° ĐŋŅ€ĐžĐąĐģĐĩĐŧĐ° ŅĐ˛ŅĐˇĐ°ĐŊĐ° Ņ ĐŊОвОК ŅĐ¸ŅŅ‚ĐĩĐŧОК Ņ€Đ°ĐˇŅ€ĐĩŅˆĐĩĐŊиК в Android. ĐĄĐēĐžŅ€ĐĩĐĩ вŅĐĩĐŗĐž, авŅ‚ĐžŅĐēŅĐŋĐžŅ€Ņ‚ ŅĐĩĐšŅ‡Đ°Ņ ĐŊĐĩ Ņ€Đ°ĐąĐžŅ‚Đ°ĐĩŅ‚. АвŅ‚Đž-ŅĐēŅĐŋĐžŅ€Ņ‚ ĐŊĐĩ вĐēĐģŅŽŅ‡ĐĩĐŊ. Bluetooth ВŅ€ĐĩĐŧŅ ĐžĐļидаĐŊиŅ ŅĐēŅ€Đ°ĐŊĐ° @@ -1904,7 +1904,7 @@ \nПоŅĐģĐĩ ŅƒŅŅ‚Đ°ĐŊОвĐēи Ņ„Đ°ĐšĐģĐ° .zip ВаŅˆ ĐąŅ€Đ°ŅĐģĐĩŅ‚ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐĩŅ€ĐĩСаĐŗŅ€ŅƒĐļĐĩĐŊ. \n \nВĐĢ ДЕЙСĐĸВĐŖЕĐĸЕ НА СВОЙ ĐĄĐĸРАĐĨ И РИСК! - БДГ-Ņ„аСа + ФаСа БДГ ХОвĐĩŅ€ŅˆĐ°ĐšŅ‚Đĩ и ĐŋŅ€Đ¸ĐŊиĐŧĐ°ĐšŅ‚Đĩ СвОĐŊĐēи ĐŋŅ€ŅĐŧĐž Ņ Ņ‡Đ°ŅĐžĐ˛ ПоĐēаСаŅ‚ŅŒ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚ĐŊŅƒŅŽ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŽ ОŅ‚ОйŅ€Đ°ĐļĐ°Ņ‚ŅŒ ĐŊĐžĐŧĐĩŅ€ Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊĐ°/иĐŧŅ СвОĐŊŅŅ‰ĐĩĐŗĐž @@ -1933,7 +1933,7 @@ ИŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ GPS Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊĐ° ОĐŋŅ€ĐĩĐ´ĐĩĐģŅŅ‚ŅŒ ĐŧĐĩŅŅ‚ĐžĐŋĐžĐģĐžĐļĐĩĐŊиĐĩ Ņ‚ĐžĐģŅŒĐēĐž ĐŋĐž ĐŋŅ€ĐžĐ˛Đ°ĐšĐ´ĐĩŅ€Ņƒ иĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚Đ°. Đ­Ņ‚Đž ĐŋОСвОĐģŅĐĩŅ‚ ŅĐŊиСиŅ‚ŅŒ ŅĐŊĐĩŅ€ĐŗĐžĐŋĐžŅ‚Ņ€ĐĩĐąĐģĐĩĐŊиĐĩ Ņ†ĐĩĐŊОК ŅĐŊиĐļĐĩĐŊиŅ Ņ‚ĐžŅ‡ĐŊĐžŅŅ‚и. ĐĸŅ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ. ВибŅ€Đ°Ņ†Đ¸Ņ ĐēĐžŅ€ĐžĐŊĐēи - НаĐŋĐžĐŧиĐŊĐ°ĐŊиĐĩ Ой иŅŅ‚ĐĩŅ‡ĐĩĐŊии AGPS + НаĐŋĐžĐŧиĐŊĐ°ĐŊиĐĩ Ой иŅŅ‚ĐĩŅ‡ĐĩĐŊии ŅŅ€ĐžĐēĐ° Đ´ĐĩĐšŅŅ‚виŅ AGPS ОбŅ€Đ°ĐąĐ°Ņ‚Ņ‹Đ˛Đ°Ņ‚ŅŒ ĐŧĐĩдиа ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ ĐŋĐĩŅ€ĐĩĐ´ ŅĐŋиŅĐēĐžĐŧ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК. ЕŅĐģи ŅŅ‚ĐžŅ‚ ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ вŅ‹ĐēĐģŅŽŅ‡ĐĩĐŊ, ĐŧĐĩдиа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ Đ´ĐžĐģĐļĐŊŅ‹ ĐąŅ‹Ņ‚ŅŒ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐĩĐŊŅ‹ в ŅĐŋиŅĐēĐĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК, Ņ‡Ņ‚ОйŅ‹ ŅĐģĐĩĐŧĐĩĐŊŅ‚Ņ‹ ŅƒĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиŅ ĐŧŅƒĐģŅŒŅ‚иĐŧĐĩдиа Ņ€Đ°ĐąĐžŅ‚Đ°Đģи ĐŊĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вĐĩ. ĐĄĐŋиŅĐžĐē ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК Ņ иĐŗĐŊĐžŅ€Đ¸Ņ€ŅƒĐĩĐŧŅ‹Đŧи ĐŧĐĩдиа ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅĐŧи Đ ĐĩĐ°ĐŗиŅ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ вĐēĐģŅŽŅ‡ĐĩĐŊĐŊĐžĐŧ ŅĐēŅ€Đ°ĐŊĐĩ @@ -1955,7 +1955,7 @@ Amazfit T-Rex 2 КоĐŊŅ‚Đ°ĐēŅ‚Ņ‹ ĐŖĐ´Đ°ĐģиŅ‚ŅŒ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚ - ЕĐļĐĩĐ´ĐŊĐĩвĐŊĐ°Ņ Ņ†ĐĩĐģŅŒ: вŅ€ĐĩĐŧŅ ŅŅ‚ĐžŅŅ‡ĐĩĐŗĐž ĐŋĐžĐģĐžĐļĐĩĐŊиŅ в ĐŧиĐŊŅƒŅ‚Đ°Ņ… + ЕĐļĐĩĐ´ĐŊĐĩвĐŊĐ°Ņ Ņ†ĐĩĐģŅŒ: вŅ€ĐĩĐŧŅ ŅŅ‚ĐžŅ в ĐŧиĐŊŅƒŅ‚Đ°Ņ… ĐĄŅ‚иĐģŅŒ ĐĨĐžŅ€Đ˛Đ°Ņ‚ŅĐēиК ЕĐļĐĩĐ´ĐŊĐĩвĐŊĐ°Ņ Ņ†ĐĩĐģŅŒ: вŅ€ĐĩĐŧŅ ŅĐļиĐŗĐ°ĐŊиŅ ĐļиŅ€Đ° в ĐŧиĐŊŅƒŅ‚Đ°Ņ… @@ -1971,7 +1971,7 @@ МиĐŊŅƒŅ‚Ņ‹ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и ĐŋĐĩŅ€ĐĩĐ´ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩĐŧ КоĐģиŅ‡ĐĩŅŅ‚вО ĐŧиĐŊŅƒŅ‚, в Ņ‚ĐĩŅ‡ĐĩĐŊиĐĩ ĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… Ņ‚Ņ€ĐĩĐŊиŅ€ĐžĐ˛ĐēĐ° Đ´ĐžĐģĐļĐŊĐ° ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ°Ņ‚ŅŒŅŅ, ĐŋŅ€ĐĩĐļĐ´Đĩ Ņ‡ĐĩĐŧ ĐžĐŊĐ° ĐąŅƒĐ´ĐĩŅ‚ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊĐ° НаŅŅ‚Ņ€ĐžĐ¸Ņ‚ŅŒ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚Ņ‹ ĐŊĐ° Ņ‡Đ°ŅĐ°Ņ… - ВŅ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹ Ņ‡Ņ‚Đž Ņ…ĐžŅ‚иŅ‚Đĩ ŅƒĐ´Đ°ĐģиŅ‚ŅŒ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚ \'%1$s\'\? + ВŅ‹ Ņ‚ĐžŅ‡ĐŊĐž Ņ…ĐžŅ‚иŅ‚Đĩ ŅƒĐ´Đ°ĐģиŅ‚ŅŒ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚ ÂĢ%1$sÂģ? КоĐŊŅ‚Đ°ĐēŅ‚ĐŊĐ°Ņ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ ФаКĐģ ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚ ĐąŅ‹Ņ‚ŅŒ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ, ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО ĐŊĐĩ ĐŋОддĐĩŅ€ĐļиваĐĩŅ‚ŅŅ. НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ĐŋОдĐĩĐģиŅ‚ŅŒŅŅ Ņ„Đ°ĐšĐģĐžĐŧ. @@ -1997,7 +1997,7 @@ КаŅ€Ņ‚ĐžŅ‡Đēи ПĐĩŅ€ŅĐžĐŊĐ°ĐģŅŒĐŊŅ‹Đš ИĐŊĐ´ĐĩĐēŅ АĐēŅ‚ивĐŊĐžŅŅ‚и MI AI - ВŅ‹Ņ‚Ņ€ŅŅ…ĐŊŅƒŅ‚ŅŒ вОдŅƒ + ВŅ‹Ņ‚ĐžĐģĐēĐŊŅƒŅ‚ŅŒ вОдŅƒ ЧĐģĐĩĐŊŅĐēиĐĩ ĐēĐ°Ņ€Ņ‚ĐžŅ‡Đēи ЧŅƒĐ˛ŅŅ‚виŅ‚ĐĩĐģŅŒĐŊŅ‹Đš Red Fantasy @@ -2007,7 +2007,7 @@ АŅŅ€ĐžĐąĐŊŅ‹Đš ŅŅ„Ņ„ĐĩĐēŅ‚ ĐŖвĐĩĐģиŅ‡ĐĩĐŊиĐĩ вŅ‹ŅĐžŅ‚Ņ‹ АŅŅ€ĐžĐąĐŊĐ°Ņ - Rush + ĐĄĐŋĐĩŅˆĐēĐ° Đ Đ°ŅŅŅ‚ĐžŅĐŊиĐĩ Ņ ĐŗĐžŅ€Ņ‹ ĐŖĐŧĐĩĐŊŅŒŅˆĐĩĐŊиĐĩ вŅ‹ŅĐžŅ‚Ņ‹ ЗоĐŊŅ‹ ŅĐĩŅ€Đ´ĐĩŅ‡ĐŊĐžĐŗĐž Ņ€Đ¸Ņ‚ĐŧĐ° @@ -2039,8 +2039,8 @@ ПодĐĩĐģиŅ‚ŅŒŅŅ НĐĩОйŅ€Đ°ĐąĐžŅ‚Đ°ĐŊĐŊОК ХвОдĐēОК ПодĐĩĐģиŅ‚ŅŒŅŅ НĐĩОйŅ€Đ°ĐąĐžŅ‚Đ°ĐŊĐŊŅ‹Đŧи ДĐĩŅ‚Đ°ĐģŅĐŧи ИĐŊŅŅ‚Ņ€ŅƒĐŧĐĩĐŊŅ‚Ņ‹ РаСŅ€Đ°ĐąĐžŅ‚Ņ‡Đ¸ĐēĐ° - ĐĄŅ‚иĐģŅŒ ŅĐģĐĩĐ´ŅƒĐĩŅ‚ Ņ†Đ¸Ņ„ĐĩŅ€ĐąĐģĐ°Ņ‚Ņƒ - ВŅ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚иŅ‚Đĩ ŅƒĐ´Đ°ĐģиŅ‚ŅŒ %d Đ°ĐēŅ‚ивĐŊĐžŅŅ‚ĐĩĐš\? + ĐĄŅ‚иĐģŅŒ ĐēĐ°Đē ĐŊĐ° Ņ†Đ¸Ņ„ĐĩŅ€ĐąĐģĐ°Ņ‚Đĩ + ВŅ‹ Ņ‚ĐžŅ‡ĐŊĐž Ņ…ĐžŅ‚иŅ‚Đĩ ŅƒĐ´Đ°ĐģиŅ‚ŅŒ %d Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и(-ĐĩĐš)? ОŅ„Ņ„ĐģĐ°ĐšĐŊ ĐŗĐžĐģĐžŅ ĐĸŅ€Đ°ĐŊŅĐģиŅ€ĐžĐ˛Đ°Ņ‚ŅŒ ИĐŊŅ‚ĐĩĐŊŅ‚Ņ‹ МĐĩдиа-ĐēĐŊĐžĐŋĐžĐē НаĐŋŅ€ŅĐŧŅƒŅŽ ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ, ĐĩŅĐģи ŅƒĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ĐŧĐĩдиа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва ĐŊĐĩ Ņ€Đ°ĐąĐžŅ‚Đ°ĐĩŅ‚ Đ´ĐģŅ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģŅ‘ĐŊĐŊŅ‹Ņ… ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК @@ -2083,7 +2083,7 @@ ĐĄĐžĐŋŅ€ŅĐļĐĩĐŊиĐĩ Ņ Ņ‡Đ°ŅĐ°Đŧи ŅƒŅĐŋĐĩŅˆĐŊĐž ĐĄĐžĐŋŅ€ŅĐļĐĩĐŊиĐĩ Ņ Ņ‡Đ°ŅĐ°Đŧи ĐŊĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ИĐŧŅ - ĐĸĐĩĐģĐĩŅ„ĐžĐŊ + НоĐŧĐĩŅ€ Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊĐ° ĐĸĐĩĐģĐĩŅ„ĐžĐŊ ĐŊĐĩ ŅƒĐēаСаĐŊ ШаĐŗОв в ŅŅ€ĐĩĐ´ĐŊĐĩĐŧ Са Đ´ĐŊи ĐŋОдŅ€ŅĐ´ ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ ĐžŅ‚ĐģĐ°Đ´ĐžŅ‡ĐŊĐžĐŗĐž ĐļŅƒŅ€ĐŊĐ°ĐģĐ° @@ -2119,10 +2119,10 @@ \nВĐŊиĐŧĐ°ĐŊиĐĩ: ĐĩŅĐģи ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž ĐžŅ„иŅ†Đ¸Đ°ĐģŅŒĐŊĐžĐĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Fossil, Đ´ĐžĐģĐŗĐžĐĩ СаĐļĐ°Ņ‚иĐĩ вĐĩŅ€Ņ…ĐŊĐĩĐš ĐēĐŊĐžĐŋĐēи Ņ‚Đ°ĐēĐļĐĩ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐĩŅ€ĐĩĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ĐžŅ‚ОйŅ€Đ°ĐļĐĩĐŊиĐĩ видĐļĐĩŅ‚Ов. ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ ŅĐ˛ĐžĐ´Đēи ĐŋĐž ŅĐŋĐžŅ€Ņ‚Ņƒ ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ Đ´Đ°ĐŊĐŊŅ‹Ņ… иĐŊĐ´ĐĩĐēŅĐ° Ņ„иС. Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и - ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ Đ´Đ°ĐŊĐŊŅ‹Ņ… Đž ĐŊĐ°ŅŅ‹Ņ‰ĐĩĐŊĐŊии ĐēŅ€ĐžĐ˛Đ¸ ĐēиŅĐģĐžŅ€ĐžĐ´ĐžĐŧ + ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ Đ´Đ°ĐŊĐŊŅ‹Ņ… Đž ĐēиŅĐģĐžŅ€ĐžĐ´Đĩ в ĐēŅ€ĐžĐ˛Đ¸ ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ Đ´Đ°ĐŊĐŊŅ‹Ņ… Đž ŅĐĩŅ€Ņ†ĐĩйиĐĩĐŊии ЖŅƒŅ€ĐŊĐ°Đģ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ - ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ вĐĩĐ´ĐĩĐŊиĐĩ ĐļŅƒŅ€ĐŊĐ°ĐģĐ° иС ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК Ņ‡Đ°ŅĐžĐ˛ + ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ вĐĩĐ´ĐĩĐŊиĐĩ ĐļŅƒŅ€ĐŊĐ°ĐģĐ° иС ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ Ņ‡Đ°ŅĐžĐ˛ ХиĐŊŅ…Ņ€ĐžĐŊиСиŅ€ŅƒĐĩĐŧŅ‹Đĩ ĐŗŅ€ŅƒĐŋĐŋŅ‹ ПŅ€ĐžŅ†ĐĩĐŊŅ‚ Đ´ĐŊĐĩĐš Ņ Đ´ĐžŅŅ‚иĐŗĐŊŅƒŅ‚ОК Ņ†ĐĩĐģŅŒŅŽ в ŅĐžĐžŅ‚ĐŊĐžŅˆĐĩĐŊии ĐēĐž вŅĐĩĐŧ Đ´ĐŊŅĐŧ Ņ ŅˆĐ°ĐŗĐ°Đŧи КоĐģиŅ‡ĐĩŅŅ‚вО Đ´ĐŊĐĩĐš Ņ Đ´ĐžŅŅ‚иĐŗĐŊŅƒŅ‚ОК Ņ†ĐĩĐģŅŒŅŽ ŅˆĐ°ĐŗОв ĐŋОдŅ€ŅĐ´ @@ -2137,16 +2137,16 @@ ПаŅƒĐˇĐ° ĐŋŅ€Đ¸ ŅĐŊŅŅ‚ии ĐŊĐ°ŅƒŅˆĐŊиĐēОв АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи ĐžŅ‚ĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ĐŋОдавĐģĐĩĐŊиĐĩ ŅˆŅƒĐŧĐ°, ĐēĐžĐŗĐ´Đ° ВŅ‹ ĐŊĐ°Ņ‡Đ¸ĐŊĐ°ĐĩŅ‚Đĩ ĐŗОвОŅ€Đ¸Ņ‚ŅŒ. ЧŅƒĐ˛ŅŅ‚виŅ‚ĐĩĐģŅŒĐŊĐžŅŅ‚ŅŒ Đ°ĐēŅ‚иваŅ†Đ¸Đ¸ ĐŋĐž ĐŗĐžĐģĐžŅŅƒ - ВŅ€ĐĩĐŧŅ иŅŅ‚ĐĩŅ‡ĐĩĐŊиŅ ĐžĐļидаĐŊиŅ + ОĐŗŅ€Đ°ĐŊиŅ‡ĐĩĐŊиĐĩ вŅ€ĐĩĐŧĐĩĐŊи ВŅ‹ŅĐžĐēĐ°Ņ НизĐēĐ°Ņ ПŅ€Đ¸ ŅĐŊŅŅ‚ии Đ ĐĩĐļиĐŧ ĐēĐŊĐžĐŋĐēи (ЛĐĩвОК) АŅ‚ĐŧĐžŅŅ„ĐĩŅ€ĐŊĐžĐĩ давĐģĐĩĐŊиĐĩ ХвОК 2 - АвŅ‚ОвŅ‹ĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ + АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēĐžĐĩ вŅ‹ĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ НĐĩ вŅ‹ĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ - Đ­Ņ‚Đ°Đŋ ĐŊĐ° ĐžŅ‚ĐēŅ€Ņ‹Ņ‚ĐžĐŧ вОСдŅƒŅ…Đĩ + ОŅ‚ĐēŅ€Ņ‹Ņ‚Đ°Ņ ŅŅ†ĐĩĐŊĐ° НĐĩйОĐģŅŒŅˆĐžĐš ИСĐŧĐĩŅ€ĐĩĐŊиĐĩ ŅƒŅĐģОвиК ĐŊĐžŅˆĐĩĐŊиŅâ€Ļ ЗавĐĩŅ€ŅˆĐĩĐŊиĐĩâ€Ļ @@ -2155,7 +2155,7 @@ АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи ĐŖĐŧĐĩŅ€ĐĩĐŊĐŊŅ‹Đš ВŅ‹ŅĐžĐēиК - ОбŅ‰Đ¸Đš иĐŊĐ´ĐĩĐēŅ Ņ„иС. Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + ВŅĐĩĐŗĐž АдаĐŋŅ‚ивĐŊŅ‹Đĩ Ņ†Đ˛ĐĩŅ‚Đ° Casio GMW-B5000 ИĐŊĐ´ĐĩĐēŅ ĐŖФ-иСĐģŅƒŅ‡ĐĩĐŊиŅ @@ -2184,7 +2184,7 @@ ПŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐēĐ° Dolby Đ´ĐģŅ ŅĐēваĐģаКСĐĩŅ€Đ° ĐĄĐĩĐŊŅĐžŅ€ĐŊĐžĐĩ ŅƒĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ПĐģавĐŊĐ°Ņ ŅĐŧĐĩĐŊĐ° ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиŅ - ВŅ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅĐžĐąŅ‹Ņ‚иŅ ŅĐĩĐŊŅĐžŅ€Đ° + ОŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐēĐ°ŅĐ°ĐŊиŅ ĐĄĐĩĐŊŅĐžŅ€ĐŊĐ°Ņ ĐąĐģĐžĐēиŅ€ĐžĐ˛ĐēĐ° КŅ€Đ°Đš двОКĐŊĐžĐŗĐž ĐēĐ°ŅĐ°ĐŊиŅ ПаŅƒĐˇĐ° вОŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиŅ @@ -2209,7 +2209,7 @@ \n \nВĐĢ ДЕЙСĐĸВĐŖЕĐĸЕ НА СВОЙ ĐĄĐĸРАĐĨ И РИСК! ГŅ€ĐžĐŧĐēĐžŅŅ‚ŅŒ ĐžĐēŅ€ŅƒĐļĐĩĐŊиŅ - ĐĸĐžĐŊ СвŅƒĐēОв ĐžĐēŅ€ŅƒĐļĐĩĐŊиŅ + ĐĸĐžĐŊĐ°ĐģŅŒĐŊĐžŅŅ‚ŅŒ СвŅƒĐēОв ĐžĐēŅ€ŅƒĐļĐĩĐŊиŅ Đ ĐĩĐļиĐŧ ŅĐģŅ‹ŅˆĐ¸ĐŧĐžŅŅ‚и ĐžĐēŅ€ŅƒĐļĐĩĐŊиŅ ĐĄĐąŅ€ĐžŅ давĐģĐĩĐŊиŅ Ņ ĐžĐēŅ€ŅƒĐļĐ°ŅŽŅ‰Đ¸Đŧ СвŅƒĐēĐžĐŧ ОŅ‚ ĐŧŅĐŗĐēĐžĐŗĐž Đē Ņ‡Ņ‘Ņ‚ĐēĐžĐŧŅƒ @@ -2226,7 +2226,7 @@ НАШЁЛ ЕГО НабоŅ€ ĐŗĐžĐģĐžŅĐžĐ˛ĐžĐš ŅĐģŅƒĐļĐąŅ‹ ПŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ŅĐž ŅĐģŅƒĐļйОК ОйŅ€Đ°ĐąĐžŅ‚Đēи ĐŗĐžĐģĐžŅĐžĐ˛Ņ‹Ņ… ĐēĐžĐŧĐ°ĐŊĐ´ - РаСŅ€ŅĐ´ ĐŗĐžĐģĐžŅĐžĐ˛ĐžĐš ŅĐģŅƒĐļĐąŅ‹ + КĐģĐ°ŅŅ ĐŗĐžĐģĐžŅĐžĐ˛ĐžĐš ŅĐģŅƒĐļĐąŅ‹ АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ СвŅƒĐēи ĐžĐēŅ€ŅƒĐļĐĩĐŊиŅ и ŅĐŊиСиŅ‚ŅŒ ĐŗŅ€ĐžĐŧĐēĐžŅŅ‚ŅŒ вОŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊиŅ ĐŋĐžŅĐģĐĩ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиŅ ĐŗĐžĐģĐžŅĐ° ОĐŗĐŊи ОŅ‚ОйŅ€Đ°ĐļĐ°Ņ‚ŅŒ ŅĐ˛ŅĐˇĐ°ĐŊĐŊŅ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва-ŅĐŋŅƒŅ‚ĐŊиĐēи @@ -2268,8 +2268,8 @@ НаĐēĐžĐŊĐĩŅ†, ŅƒŅŅ‚Đ°ĐŊОвиŅ‚Đĩ ŅŅ‚Ņ€ĐĩĐģĐēŅƒ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и ĐŊĐ° 100%. ПоĐļĐ°ĐģŅƒĐšŅŅ‚Đ°, ŅƒŅ‡Ņ‚иŅ‚Đĩ, Ņ‡Ņ‚Đž ĐžĐŊĐ° двиĐļĐĩŅ‚ŅŅ Ņ‚ĐžĐģŅŒĐēĐž в ОдĐŊĐžĐŧ ĐŊĐ°ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊии. ĐĄĐģĐĩĐ´ŅƒŅŽŅ‰Đ°Ņ ПоĐģĐŊĐ°Ņ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊиСаŅ†Đ¸Ņ - ĐĄŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ° ĐŧĐĩĐŊŅŽ JSON, СадаĐŊĐŊĐ°Ņ в GB - ОŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ ĐŧĐĩĐŊŅŽ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ ŅĐŋŅƒŅ‚ĐŊиĐēĐ° + ĐĄŅ‚Ņ€ŅƒĐēŅ‚ŅƒŅ€Đ° ĐŧĐĩĐŊŅŽ JSON, СадаĐŊĐŊĐ°Ņ в ГБ + ОŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ ĐŧĐĩĐŊŅŽ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ-ŅĐŋŅƒŅ‚ĐŊиĐēĐ° ĐĄĐēĐžŅ€ĐĩĐĩ вŅĐĩĐŗĐž, ÂĢĐĄĐŋŅƒŅ‚ĐŊиĐē ĐŧĐĩĐŊŅŽ HRÂģ‎ ĐŊĐĩ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž ДĐģŅ ŅƒĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиŅ йОĐŊŅƒŅĐŊŅ‹Đŧи ĐēĐ°Ņ€Ņ‚Đ°Đŧи Ņ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ Catima ОŅ‚ŅŅƒŅ‚ŅŅ‚вŅƒŅŽŅ‰Đ¸Đĩ Ņ€Đ°ĐˇŅ€ĐĩŅˆĐĩĐŊиŅ @@ -2324,7 +2324,7 @@ ОŅŅ‚Đ°ĐŊОвĐēĐ° ŅĐģŅƒĐļĐąŅ‹ Ņ€Đ°ŅĐŋĐžĐģĐžĐļĐĩĐŊиŅ GPS ###,# ĐŧиĐģŅŒ БĐĩŅĐŋŅ€ĐĩŅ€Ņ‹Đ˛ĐŊĐ°Ņ ĐŋĐžŅĐģĐĩдОваŅ‚ĐĩĐģŅŒĐŊĐžŅŅ‚ŅŒ Đ´ĐŊĐĩĐš Ņ Đ´ĐžŅŅ‚иĐļĐĩĐŊиĐĩĐŧ Ņ†ĐĩĐģи ĐŋĐž ŅˆĐ°ĐŗĐ°Đŧ - ĐĸŅ€Đ°ĐŊŅĐģŅŅ†Đ¸Ņ ĐŋĐž ŅĐēŅĐŋĐžŅ€Ņ‚Ņƒ БД + ĐĸŅ€Đ°ĐŊŅĐģŅŅ†Đ¸Ņ ĐŋĐž ŅĐēŅĐŋĐžŅ€Ņ‚Ņƒ йаСŅ‹ Đ´Đ°ĐŊĐŊŅ‹Ņ… ПозвоĐģŅĐĩŅ‚ Đ°ĐēŅ‚ивиŅ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅĐēŅĐŋĐžŅ€Ņ‚ йаСŅ‹ Đ´Đ°ĐŊĐŊŅ‹Ņ… Ņ‡ĐĩŅ€ĐĩС API Intent ОŅ‚ĐŋŅ€Đ°Đ˛ĐēĐ° ŅƒĐēаСаĐŊиК ĐŊавиĐŗĐ°Ņ†Đ¸ĐžĐŊĐŊŅ‹Ņ… ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК ĐŊĐ° Ņ‡Đ°ŅŅ‹ РаСŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ĐžŅ‚ĐģĐ°Đ´ĐžŅ‡ĐŊŅ‹Đĩ ĐēĐžĐŧĐ°ĐŊĐ´Ņ‹ @@ -2365,7 +2365,7 @@ ЗаŅŅ‡Đ¸Ņ‚Ņ‹Đ˛Đ°Ņ‚ŅŒ двОКĐŊĐžĐĩ ĐēĐ°ŅĐ°ĐŊиĐĩ, Đ´Đ°ĐļĐĩ ĐĩŅĐģи ĐžĐŊĐž ĐąŅ‹ĐģĐž ĐŋĐž ŅĐēŅ€Đ°ĐŊŅƒ ВŅ€ĐĩĐŧŅ, ĐēĐžĐŗĐ´Đ° ŅĐžĐąĐ¸Ņ€Đ°ĐĩŅ‚ŅŅ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Đž ŅĐŊĐĩ ЗавĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ ĐŋĐžŅĐģĐĩ Ņ‚иŅˆĐ¸ĐŊŅ‹ ĐŊĐ° ĐŋŅ€ĐžŅ‚ŅĐļĐĩĐŊии: - ДĐŊĐĩвĐŊĐžĐĩ ŅƒĐ˛ĐĩĐģиŅ‡ĐĩĐŊиĐĩ иĐŊĐ´ĐĩĐēŅĐ° Ņ„иСиŅ‡ĐĩŅĐēОК Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + ПŅ€Đ¸Ņ€ĐžŅŅ‚ Са Đ´ĐĩĐŊŅŒ ИŅĐŋĐžĐģŅŒĐˇŅƒĐšŅ‚Đĩ ĐŊĐ°ŅƒŅˆĐŊиĐēи ĐŋĐž-ОйŅ‹Ņ‡ĐŊĐžĐŧŅƒ. ЕŅĐģи ŅƒŅĐģОвиŅ ĐŊĐžŅˆĐĩĐŊиŅ иĐģи Đ°Ņ‚ĐŧĐžŅŅ„ĐĩŅ€ĐŊĐžĐĩ давĐģĐĩĐŊиĐĩ иСĐŧĐĩĐŊŅŅ‚ŅŅ, СаĐŋŅƒŅŅ‚иŅ‚Đĩ ĐžĐŋŅ‚иĐŧиСаŅ†Đ¸ŅŽ ĐĩŅ‰Ņ‘ Ņ€Đ°Đˇ. РаСĐŗОвОŅ€ ĐŋŅ€Đ¸ ĐŊĐžŅˆĐĩĐŊии ĐŗĐ°Ņ€ĐŊиŅ‚ŅƒŅ€Ņ‹ ХвОŅ ĐŋŅ€ĐĩĐ´ŅƒŅŅ‚Đ°ĐŊОвĐēĐ° 1 @@ -2382,4 +2382,375 @@ ОŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ Ņ‚ĐĩĐēŅŅ‚ ĐŊĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО ĐĄŅ‚ĐĩĐŋĐŋĐĩŅ€ ЗаŅ€ŅĐ´ĐŊŅ‹Đš Ņ„ŅƒŅ‚ĐģŅŅ€ + ĐĸŅ€Đ¸ĐļĐ´Ņ‹ + ВŅ€ĐĩĐŧŅ вŅ‹ĐēĐģŅŽŅ‡ĐĩĐŊиŅ + КоŅ€ĐžŅ‚ĐēĐ°Ņ + КаĐģĐžŅ€Đ¸Đ¸ + БĐĩС иĐŊдиĐēĐ°Ņ‚ĐžŅ€Đ° + ĐĸĐĩĐēŅƒŅ‰Đ°Ņ MTU %1$d ŅĐģиŅˆĐēĐžĐŧ ĐŊиСĐēĐ°Ņ. ПоĐļĐ°ĐģŅƒĐšŅŅ‚Đ°, вĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ вŅ‹ŅĐžĐēŅƒŅŽ MTU в ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва и ĐŋĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ĐĩĐŗĐž. + Riiiver + ПŅ€ĐžĐ´ĐžĐģĐļиŅ‚ĐĩĐģŅŒĐŊĐ°Ņ + КоĐģ-вО ŅˆĐ°ĐŗОв + По ĐĻĐĩĐģŅŒŅĐ¸ŅŽ + ПŅ€ĐžŅŅ‚Đ°Ņ + ДваĐļĐ´Ņ‹ + БŅ‹ŅŅ‚Ņ€Đ°Ņ + ЕдиĐŊиŅ†Đ° иСĐŧĐĩŅ€ĐĩĐŊиŅ Ņ‚ĐĩĐŧĐŋĐĩŅ€Đ°Ņ‚ŅƒŅ€Ņ‹ + ЕдиĐŊĐžĐļĐ´Ņ‹ + Wena Pay + ДĐĩĐšŅŅ‚виĐĩ ĐēĐŊĐžĐŋĐēи + ВибŅ€Đ°Ņ†Đ¸Ņ ĐŋŅ€Đ¸ вŅ…ОдŅŅ‰Đ¸Ņ… СвОĐŊĐēĐ°Ņ… + ĐĻвĐĩŅ‚ иĐŊдиĐēĐ°Ņ‚ĐžŅ€Đ° ĐŋŅ€Đ¸ вŅ…ОдŅŅ‰ĐĩĐŧ СвОĐŊĐēĐĩ + ХиŅ€ĐĩĐŊĐ° + ШĐēĐ°ĐģĐ° иСĐŧĐĩŅ€ĐĩĐŊиŅ Ņ‚ĐĩĐŧĐŋĐĩŅ€Đ°Ņ‚ŅƒŅ€Ņ‹, иŅĐŋĐžĐģŅŒĐˇŅƒĐĩĐŧĐ°Ņ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вОĐŧ. + ПовŅ‚ĐžŅ€ĐĩĐŊиŅ вийŅ€Đ°Ņ†Đ¸Đ¸ ĐŋŅ€Đ¸ вŅ…ОдŅŅ‰Đ¸Ņ… + ПоŅŅ‚ĐžŅĐŊĐŊĐž + ЧĐĩŅ‚Ņ‹Ņ€ĐĩĐļĐ´Ņ‹ + По ФаŅ€ĐĩĐŊĐŗĐĩĐšŅ‚Ņƒ + КаŅ€Ņ‚Ņ‹ ĐžĐŋĐģĐ°Ņ‚Ņ‹ + ĐĻвĐĩŅ‚ иĐŊдиĐēĐ°Ņ‚ĐžŅ€Đ° ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК + АвŅ‚ОвŅ‹ĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ + ПŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´ĐĩĐŊиĐĩ + НаŅŅ‚Ņ€ĐžĐšĐēи ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК ĐžŅ‚ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК + БĐĩС вийŅ€Đ°Ņ†Đ¸Đ¸ + ĐĸŅ€ĐžĐšĐŊĐ°Ņ + НаŅŅ‚Ņ€ĐžĐšĐēи ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК + НаŅŅ‚Ņ€ĐžĐšĐēи вŅ…ОдŅŅ‰Đ¸Ņ… СвОĐŊĐēОв + ЗаĐŋŅƒŅŅ‚иŅ‚ŅŒ Ņ‚Đ°ĐšĐŧĐĩŅ€ + ИĐēĐžĐŊĐēĐ° \"НаŅŅ‚Ņ€ĐžĐšĐēи\" вŅĐĩĐŗĐ´Đ° ĐžŅ‚ОйŅ€Đ°ĐļĐ°ĐĩŅ‚ŅŅ в ĐēĐžĐŊŅ†Đĩ ĐŧĐĩĐŊŅŽ + ЗаĐēŅ€Ņ‹Ņ‚ŅŒ СаĐŧĐžĐē Qrio + ЛĐĩвŅ‹Đš + НиŅŅ…ОдŅŅ‰Đ°Ņ + ĐĄĐŧĐ°Ņ€Ņ‚-вийŅ€Đ°Ņ†Đ¸Ņ + РаСŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ Wena ĐŋĐĩŅ€Đ¸ĐžĐ´Đ¸Ņ‡ĐĩŅĐēи ĐŋŅ€ĐžŅĐ¸Ņ‚ŅŒ Gadgetbridge ŅĐēĐ°Ņ‡Đ°Ņ‚ŅŒ ĐŊĐ°ĐēĐžĐŋĐģĐĩĐŊĐŊŅƒŅŽ иŅŅ‚ĐžŅ€Đ¸ŅŽ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и Ņ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва + БаĐģĐ°ĐŊŅ ĐŊĐ° Suica + ĐĄŅ€ĐĩĐ´ĐŊĐĩ + НаŅŅ‚Ņ€ĐžĐšĐēи Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + Qrio + БĐĩĐģŅ‹Đš + ĐĸĐĩĐēŅƒŅ‰ĐĩĐĩ вŅ€ĐĩĐŧŅ + ХиĐŊŅ…Ņ€ĐžĐŊиСаŅ†Đ¸Ņ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и в Ņ„ĐžĐŊĐĩ + ĐĄĐģайО + ЗĐŊĐ°Ņ‡Đēи ĐŊĐ° ĐŗĐģавĐŊĐžĐŧ ŅĐēŅ€Đ°ĐŊĐĩ + ПоĐŗОда в ŅŅ‚Đ°Ņ‚ŅƒŅ-йаŅ€Đĩ + ВоŅŅ…ОдŅŅ‰Đ°Ņ + ПŅ€Đ°Đ˛Ņ‹Đš + НаŅŅ‚Ņ€ĐžĐšĐēи ĐžŅ‚ОйŅ€Đ°ĐļĐĩĐŊиŅ + КŅ€ŅƒĐŋĐŊŅ‹Đš ŅˆŅ€Đ¸Ņ„Ņ‚ + Đ­ĐŊĐĩŅ€ĐŗиŅ + ĐŖŅŅ‚Ņ€ĐžĐšŅŅ‚вО ĐąŅƒĐ´ĐĩŅ‚ вŅ‹ĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ и вĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи в СадаĐŊĐŊĐžĐĩ вŅ€ĐĩĐŧŅ + ВĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ ŅĐēŅ€Đ°ĐŊ ĐŋŅ€Đ¸ ĐŋОвОŅ€ĐžŅ‚Đĩ СаĐŋŅŅŅ‚ŅŒŅ + ĐŖвĐĩĐģиŅ‡Đ¸Đ˛Đ°ĐĩŅ‚ Ņ€Đ°ĐˇĐŧĐĩŅ€ Ņ‚ĐĩĐēŅŅ‚Đ° в ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Đĩ, ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅŅ…, и Ņ‚.Đ´. + ДвойĐŊĐžĐĩ ĐŊĐ°ĐļĐ°Ņ‚иĐĩ + ОŅ‚ĐēŅ€Ņ‹Ņ‚ŅŒ СаĐŧĐžĐē Qrio + ПŅ€Đ¸ĐŊиĐŧĐ°Ņ‚ŅŒ вŅ…ОдŅŅ‰Đ¸Đĩ СвОĐŊĐēи + ПоĐēаСŅ‹Đ˛Đ°Ņ‚ŅŒ иĐēĐžĐŊĐēŅƒ Ņ‚ĐĩĐēŅƒŅ‰ĐĩĐš ĐŋĐžĐŗОдŅ‹ в ĐģĐĩвОĐŧ вĐĩŅ€Ņ…ĐŊĐĩĐŧ ŅƒĐŗĐģŅƒ ĐŗĐģавĐŊĐžĐŗĐž ŅĐēŅ€Đ°ĐŊĐ° + ВŅ€ĐĩĐŧŅ вĐēĐģŅŽŅ‡ĐĩĐŊиŅ + ЗĐĩĐģŅ‘ĐŊŅ‹Đš + ЖŅ‘ĐģŅ‚Ņ‹Đš + КŅ€Đ°ŅĐŊŅ‹Đš + БиŅ€ŅŽĐˇĐžĐ˛Ņ‹Đš + ЗĐŊĐ°Ņ‡Đēи ĐŧĐĩĐŊŅŽ + ВибŅ€Đ°Ņ†Đ¸Ņ Đ´ĐģŅ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК + ХиĐģĐ° вийŅ€Đ°Ņ†Đ¸Đ¸ + ЕŅĐģи вŅ‹ĐēĐģŅŽŅ‡ĐĩĐŊĐž, Ņ‚Đž вŅ…ОдŅŅ‰Đ¸Đĩ СвОĐŊĐēи ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐžŅ‚ОйŅ€Đ°ĐļĐ°Ņ‚ŅŒŅŅ ĐŊĐ° Wena + НаĐļĐ°Ņ‚иĐĩ Ņ ŅƒĐ´ĐĩŅ€ĐļĐ°ĐŊиĐĩĐŧ + ПоŅ€ŅĐ´ĐžĐē ŅŅ‚Ņ€Đ°ĐŊиŅ† ŅŅ‚Đ°Ņ‚ŅƒŅĐ° + НаŅŅ‚Ņ€ĐžĐšĐēи ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐēĐ° + ПовŅ‚ĐžŅ€ вийŅ€Đ°Ņ†Đ¸Đ¸ ĐŋŅ€Đ¸ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅŅ… + ĐĄŅ€ĐĩĐ´ĐŊиК + ДобавĐģŅĐĩŅ‚ ĐŋŅ€ŅĐŧĐžŅƒĐŗĐžĐģŅŒĐŊŅ‹Đš Ņ„ĐžĐŊ вОĐēŅ€ŅƒĐŗ иĐēĐžĐŊĐžĐē ĐŊĐ° ĐŗĐģавĐŊĐžĐŧ ŅĐēŅ€Đ°ĐŊĐĩ + БаĐģĐ°ĐŊŅ ĐŊĐ° Edy + ЗаĐŋĐ°Ņ Đ´ĐģŅ ŅƒĐŧĐŊĐžĐŗĐž ĐŋŅ€ĐžĐąŅƒĐļĐ´ĐĩĐŊиŅ + АĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚ивĐŊŅ‹Đš диСаКĐŊ + НаŅ‡Đ¸ĐŊĐ°Ņ‚ŅŒ Đ´ĐĩĐŊŅŒ в + ФиОĐģĐĩŅ‚ОвŅ‹Đš + ХиĐģŅŒĐŊĐž + ХиĐŊиК + ОŅ‚ОйŅ€Đ°ĐļĐĩĐŊиĐĩ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + НаŅ‡Đ°ĐģĐž ĐžŅ‚ŅĐģĐĩĐļиваĐŊиŅ Ņ„иŅ‚ĐŊĐĩŅ-ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩĐŧ + Mi Band HRX (ĐąĐĩС Đ´Đ°Ņ‚Ņ‡Đ¸ĐēĐ° ЧХХ) + ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŊОвОĐĩ Đ´ĐĩĐšŅŅ‚виĐĩ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиŅ, ĐēĐžŅ‚ĐžŅ€ĐžĐĩ Đ´ĐžĐģĐļĐŊĐž ŅƒŅŅ‚Ņ€Đ°ĐŊиŅ‚ŅŒ ĐŋŅ€ĐžĐąĐģĐĩĐŧŅ‹ Ņ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚в. ОŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ŅŅ‚Ņƒ Ņ„ŅƒĐŊĐēŅ†Đ¸ŅŽ, ĐĩŅĐģи Ņƒ ваŅ вОСĐŊиĐēĐŊŅƒŅ‚ ĐēĐ°ĐēиĐĩ-ĐģийО ĐŋŅ€ĐžĐąĐģĐĩĐŧŅ‹ ĐŋŅ€Đ¸ ĐŋОиŅĐēĐĩ иĐģи ŅĐžĐŋŅ€ŅĐļĐĩĐŊии Ņ ваŅˆĐ¸Đŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вОĐŧ. + ĐŋĐĩŅ€ĐĩŅ‚Đ°ŅĐēиваĐŊиĐĩ ŅĐģĐĩĐŧĐĩĐŊŅ‚Ов + ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŊОвОĐĩ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + Sony Wena 3 + ВŅ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ двиĐļĐĩĐŊиŅ Ņ€ŅƒĐē ĐŋŅ€Đ¸ ŅĐŊŅŅ‚ĐžĐŧ Ņ€ĐĩĐŧĐĩŅˆĐēĐĩ + ОŅŅ‚Đ°ĐŊОвĐēĐ° ĐžŅ‚ŅĐģĐĩĐļиваĐŊиŅ Ņ„иŅ‚ĐŊĐĩŅ-ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩĐŧ + ДиаĐŋаСОĐŊŅ‹ + Sony WF-1000XM5 + МŅĐŗĐēиК + ВзвоĐģĐŊОваĐŊĐŊŅ‹Đš + ЗабĐģĐžĐēиŅ€ĐžĐ˛Đ°Ņ‚ŅŒ + ПовŅ‹ŅˆĐĩĐŊиĐĩ вŅ‹ŅĐžĐēиŅ… Ņ‡Đ°ŅŅ‚ĐžŅ‚ + ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐžŅ‚ŅĐģĐĩĐļиваĐŊиĐĩ Ņ„иŅ‚ĐŊĐĩŅ-ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ + ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅĐŋŅ€Đ°Đ˛Đ° + ПĐĩŅ€ĐĩĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ŅƒĐŋŅ€Đ°Đ˛ĐģĐĩĐŊиĐĩ ŅĐģĐĩва + ДвиĐļĐĩĐŊиĐĩ Ņ€ŅƒĐē + БŅ‹ŅŅ‚Ņ€Ņ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи СвŅƒĐēĐ° ĐžĐēŅ€ŅƒĐļĐĩĐŊиŅ + ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ Ņ‚ĐžĐģŅŒĐēĐž Ņ‚ĐĩĐģĐž + ЛаŅ‚Ņ‹ŅˆŅĐēиК + ОбŅ‹Ņ‡ĐŊŅ‹Đĩ ŅĐ¸ĐŧвОĐģŅ‹ + ИĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ Ņ€Đ°ĐąĐžŅ‡ĐĩĐŗĐž ĐŋŅ€ĐžŅ„иĐģŅ + НĐĩ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК иС Ņ€Đ°ĐąĐžŅ‡ĐĩĐŗĐž ĐŋŅ€ĐžŅ„иĐģŅ ĐŊĐ° Ņ‡Đ°ŅŅ‹ + Đ¯Ņ€ĐēĐž-ĐąĐĩĐģŅ‹Đš + ĐŖĐģŅŒŅ‚иĐŧĐ° + ГоŅ€ĐžĐ´ ĐĄĐēĐžŅ€ĐžŅŅ‚и + НаŅƒŅˆĐŊиĐē + НоŅ€ĐŧĐ°ĐģŅŒĐŊŅ‹Đš (60-90 ŅĐĩĐē.) + Free combination + ĐĄĐžŅŅ‚ав Ņ‚ĐĩĐģĐ° + Zepp Pay + БŅ‹ŅŅ‚Ņ€Ņ‹Đš (30 ŅĐĩĐē.) + ЗвŅ‘СдĐŊĐžĐĩ ĐŊĐĩйО + Amazfit Balance + Đ ĐĩĐļиĐŧ иСĐŧĐĩŅ€ĐĩĐŊиŅ + ĐĸĐžŅ‡ĐŊŅ‹Đš (3 ĐŧиĐŊ.) + НĐĩОйŅŠŅŅ‚ĐŊĐžĐĩ ĐŊĐĩйО + Đ¯Ņ€ĐģŅ‹Đēи Ņ‚Ņ€ĐĩĐŊиŅ€ĐžĐ˛ĐžĐē + ХвĐĩŅ€ĐēĐ°ĐŊиĐĩ ĐŧĐžĐģĐŊии + Đ¯Ņ€ĐģŅ‹Đēи ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК + Đ ŅƒĐēОвОдŅŅ‚вО + ГоŅ‚ОвĐŊĐžŅŅ‚ŅŒ + ĐĸĐĩŅ€ĐŧĐžĐŧĐĩŅ‚Ņ€ + Femometer Vinca II + НавиĐŗĐ°Ņ†Đ¸Ņ ĐąŅ‹ĐģĐ° ĐŊĐ°Ņ‡Đ°Ņ‚Đ°, ĐŊĐž navigationApp ĐŊĐĩ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž ĐŊĐ° Ņ‡Đ°ŅĐ°Ņ…. ПоĐļĐ°ĐģŅƒĐšŅŅ‚Đ°, ŅƒŅŅ‚Đ°ĐŊОвиŅ‚Đĩ ĐĩĐŗĐž в ĐŧĐĩĐŊĐĩĐ´ĐļĐĩŅ€Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК. + ПŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Đ´ĐģŅ ĐŊавиĐŗĐ°Ņ†Đ¸Đ¸ ĐŊĐĩ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž ĐŊĐ° Ņ‡Đ°ŅĐ°Ņ… + ИĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ (ĐŋŅ€ĐĩĐēŅ€Đ°Ņ‚иŅ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ) + ОŅ‚ĐēĐģĐžĐŊиŅ‚ŅŒ + ĐĄĐŋĐžŅĐžĐą ĐžŅ‚ĐēĐģĐžĐŊĐĩĐŊиŅ вŅ‹ĐˇĐžĐ˛Đ° + ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ ŅŅ‚Đ°Ņ‚иŅŅ‚иĐēи + ĐŋĐģĐžŅ‰Đ°Đ´ĐēĐ° + КаĐēĐžĐĩ Đ´ĐĩĐšŅŅ‚виĐĩ ĐŊĐĩОйŅ…ОдиĐŧĐž ŅĐžĐ˛ĐĩŅ€ŅˆĐ¸Ņ‚ŅŒ ĐŋŅ€Đ¸ ĐžŅ‚ĐēĐģĐžĐŊĐĩĐŊии вŅ‹ĐˇĐžĐ˛Đ° Ņ Ņ‡Đ°ŅĐžĐ˛ + ПоĐģŅƒŅ‡ĐĩĐŊиĐĩ Ņ‚ĐĩĐŧĐŋĐĩŅ€Đ°Ņ‚ŅƒŅ€Ņ‹ + ДĐģиĐŊĐ° ĐŋĐžĐģĐžŅŅ‹ + Amazfit Active Edge + ĐĄŅ€ĐĩĐ´ĐŊиК ŅƒŅ€ĐžĐ˛ĐĩĐŊŅŒ ĐēиŅĐģĐžŅ€ĐžĐ´Đ° в ĐēŅ€ĐžĐ˛Đ¸ + Amazfit Active + НавиĐŗĐ°Ņ†Đ¸ĐžĐŊĐŊŅ‹Đĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ + НаŅŅ‚Ņ€ĐžĐšĐēи ĐŊавиĐŗĐ°Ņ†Đ¸Đ¸ + Google КаŅ€Ņ‚Ņ‹ + НаŅ‡Đ¸ĐŊĐ°Ņ‚ŅŒ СаĐŗĐžĐģОвОĐē ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ Ņ ĐŊаСваĐŊиŅ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ-иŅŅ‚ĐžŅ‡ĐŊиĐēĐ° + ИŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ вŅ‹ĐąĐžŅ€Đ° иŅĐŋĐžĐģŅŒĐˇŅƒĐĩĐŧОК вĐĩŅ€ŅĐ¸Đ¸ OsmAnd + НазваĐŊиĐĩ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ в ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊии + ИĐŧŅ ĐŋĐ°ĐēĐĩŅ‚Đ° OsmAnd + OsmAnd(+) + ОĐŋОвĐĩŅ‰ĐĩĐŊиŅ ĐŋŅ€ĐžŅ‡Đ¸Ņ… ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиК + ОĐŋОвĐĩŅ‰ĐĩĐŊиŅ ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ + ОĐŋОвĐĩŅ‰Đ°Ņ‚ŅŒ Đž вŅ…ОдŅŅ‰Đ¸Ņ… СвОĐŊĐēĐ°Ņ… вийŅ€Đ°Ņ†Đ¸ĐĩĐš/ĐŋиŅ‰Đ°ĐŊиĐĩĐŧ + Xiaomi Smart Band 8 + БĐĩĐŗ + Đ Đ°ŅĐŋиŅĐ°ĐŊиĐĩ Ņ€ĐĩĐļиĐŧĐ° ŅĐŊĐ° + ОĐŋОвĐĩŅ‰ĐĩĐŊиŅ Đž вŅ…ОдŅŅ‰Đ¸Ņ… + НаŅ‡Đ°ĐģĐž ŅĐŊĐ° + ОĐŋОвĐĩŅ‰ĐĩĐŊиŅ ŅĐģ. ĐŋĐžŅ‡Ņ‚Ņ‹ + ОĐŋОвĐĩŅ‰Đ°Ņ‚ŅŒ Đž ĐŊОвŅ‹Ņ… ĐŋиŅŅŒĐŧĐ°Ņ… вийŅ€Đ°Ņ†Đ¸ĐĩĐš/ĐŋиŅ‰Đ°ĐŊиĐĩĐŧ + КоĐŊŅ†ĐĩĐŊŅ‚Ņ€Đ°Ņ†Đ¸Ņ + ОĐŋОвĐĩŅ‰Đ°Ņ‚ŅŒ Đž ĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐŊŅ‹Ņ… SMS (Ņ‚ĐĩĐēŅŅ‚ОвŅ‹Ņ…) ŅĐžĐžĐąŅ‰ĐĩĐŊиŅŅ… вийŅ€Đ°Ņ†Đ¸ĐĩĐš/ĐŋиŅ‰Đ°ĐŊиĐĩĐŧ + ПоĐēаСŅ‹Đ˛Đ°Ņ‚ŅŒ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ в СаĐŗĐžĐģОвĐēĐĩ + Redmi Watch 3 Active + ОĐŋОвĐĩŅ‰Đ°Ņ‚ŅŒ Đž ŅĐžĐąŅ‹Ņ‚иŅŅ… ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ вийŅ€Đ°Ņ†Đ¸ĐĩĐš/ĐŋиŅ‰Đ°ĐŊиĐĩĐŧ + ĐĄĐĩŅ€Đ¸ĐšĐŊŅ‹Đš ĐŊĐžĐŧĐĩŅ€ + ДаŅ‚ŅĐēиК + ПŅ€ĐžĐąŅƒĐļĐ´ĐĩĐŊиĐĩ + ĐĄŅ‚Đ°Ņ‚иŅŅ‚иĐēĐ° + ОĐŋОвĐĩŅ‰Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ ĐŋŅ€ĐžŅ‡Đ¸Ņ… ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅŅ… вийŅ€Đ°Ņ†Đ¸ĐĩĐš/ĐŋиŅ‰Đ°ĐŊиĐĩĐŧ + Xiaomi Watch Lite + ПоĐēаСŅ‹Đ˛Đ°Ņ‚ŅŒ ŅĐžĐžĐąŅ‰ĐĩĐŊиĐĩ в СаĐŗĐžĐģОвĐēĐĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ в ŅĐžĐžŅ‚вĐĩŅ‚ŅŅ‚вии Ņ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Đŧи ĐēĐžĐŊŅ„идĐĩĐŊŅ†Đ¸Đ°ĐģŅŒĐŊĐžŅŅ‚и ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва + ОŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ĐŊĐ°ĐŋĐžĐŧиĐŊĐ°ĐŊиĐĩ и вОКŅ‚и в Ņ€ĐĩĐļиĐŧ ŅĐŊĐ° вО вŅ€ĐĩĐŧŅ ĐŊĐ°Ņ‡Đ°ĐģĐ° ŅĐŊĐ°. АĐēŅ‚ивиŅ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐē ĐŋŅ€Đ¸ СадаĐŊĐŊĐžĐŧ вŅ€ĐĩĐŧĐĩĐŊи ĐŋŅ€ĐžĐąŅƒĐļĐ´ĐĩĐŊиŅ. + ОĐŋОвĐĩŅ‰ĐĩĐŊиŅ SMS-ŅĐžĐžĐąŅ‰ĐĩĐŊиК + ОĐŋОвĐĩŅ‰ĐĩĐŊиŅ + Xiaomi Smart Band 7 Pro + Xiaomi Watch S1 Active + Mi Watch Color Sports + Pixoo + ПĐĩŅ€ĐĩдаваŅ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиК ĐŊĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО + ПĐĩŅ€ĐĩдаваŅ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиŅ + ĐĸŅ€ĐĩĐšĐģŅ€Đ°ĐŊĐŊиĐŊĐŗ + МаĐēĐĩŅ‚ видĐļĐĩŅ‚Đ° + На ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вĐĩ ĐŊĐĩ ĐžŅŅ‚Đ°ĐģĐžŅŅŒ ŅĐ˛ĐžĐąĐžĐ´ĐŊŅ‹Ņ… ĐŧĐĩŅŅ‚ ĐŋОд ŅĐēŅ€Đ°ĐŊŅ‹ видĐļĐĩŅ‚Ов (вŅĐĩĐŗĐž: %1$s) + НĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ - ВĐēĐģ. + БĐĩССвŅƒŅ‡ĐŊŅ‹Đš Ņ€ĐĩĐļиĐŧ Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊĐ° + НĐĩиСвĐĩŅŅ‚ĐŊŅ‹Đš Ņ‚иĐŋ Ņ‚Ņ€ĐĩĐŊиŅ€ĐžĐ˛Đēи - %s + ĐĸиĐŋ ĐŊĐžŅˆĐĩĐŊиŅ + Pebble (ĐŊĐ° ОйŅƒĐ˛Đ¸) + ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚иŅ‚ŅŒ вŅ‹ŅˆĐĩ + ВŅ‹ Ņ‚ĐžŅ‡ĐŊĐž Ņ…ĐžŅ‚иŅ‚Đĩ ŅƒĐ´Đ°ĐģиŅ‚ŅŒ ÂĢ%1$sÂģ? + НоŅ€ĐŧĐ°ĐģŅŒĐŊŅ‹Đš / На вийŅ€Đ°Ņ†Đ¸Đ¸ + ДоĐŋĐžĐģĐŊиŅ‚ĐĩĐģŅŒĐŊĐ°Ņ Ņ†ĐĩĐģŅŒ + ОŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ, ĐēĐžĐŗĐ´Đ° ŅƒŅ€ĐžĐ˛ĐĩĐŊŅŒ ĐļиСĐŊĐĩĐŊĐŊОК ŅĐŊĐĩŅ€Đŗии Са ĐŋĐžŅĐģĐĩĐ´ĐŊиĐĩ 7 Đ´ĐŊĐĩĐš Đ´ĐžŅŅ‚иĐŗĐ°ĐĩŅ‚ 30, 60 иĐģи 100 + ГоŅ€ĐŊŅ‹Đš ĐŋĐžŅ…Од + ĐŖŅ€ĐžĐ˛ĐĩĐŊŅŒ ĐļиСĐŊĐĩĐŊĐŊОК ŅĐŊĐĩŅ€Đŗии + 2 видĐļĐĩŅ‚Đ° + ĐŖĐ´Đ°ĐģиŅ‚ŅŒ ŅĐēŅ€Đ°ĐŊ видĐļĐĩŅ‚Đ° + НĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ - ĐĸĐžĐģŅŒĐēĐž ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐē + 1 ĐŊавĐĩŅ€Ņ…Ņƒ, 2 вĐŊиСŅƒ + 2 ĐŊавĐĩŅ€Ņ…Ņƒ, 2 вĐŊиСŅƒ + ДоĐģĐļĐŊĐž ĐąŅ‹Ņ‚ŅŒ ĐŧиĐŊиĐŧŅƒĐŧ %1$s ŅĐēŅ€Đ°ĐŊОв + На вийŅ€Đ°Ņ†Đ¸Đ¸ / БĐĩССвŅƒŅ‡ĐŊŅ‹Đš + ВидĐļĐĩŅ‚ + ОŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŋŅ€Đ¸ Đ´ĐžŅŅ‚иĐļĐĩĐŊии ĐŧĐ°ĐēŅĐ¸ĐŧĐ°ĐģŅŒĐŊĐžĐŗĐž ŅƒŅ€ĐžĐ˛ĐŊŅ ĐļиС. ŅĐŊĐĩŅ€Đŗии Са Đ´ĐĩĐŊŅŒ + Redmi Smart Band 2 + Đ­ĐēŅ€Đ°ĐŊ видĐļĐĩŅ‚Đ° + ПĐĩŅ€ĐĩĐŧĐĩŅŅ‚иŅ‚ŅŒ ĐŊиĐļĐĩ + 1 видĐļĐĩŅ‚ + НĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ - ĐĸĐžĐģŅŒĐēĐž ваĐļĐŊŅ‹Đĩ + НĐĩ СадаĐŊĐž + ПодŅ‚иĐŋ видĐļĐĩŅ‚Đ° + ВŅ€ĐĩĐŧŅ ŅŅ‚ĐžŅ + ВŅ€ĐĩĐŧŅ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + НаŅ€ŅƒŅ‡ĐŊŅ‹Đš ĐąŅ€Đ°ŅĐģĐĩŅ‚ + Đ­ĐēŅ€Đ°ĐŊ %s + ВŅ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ вŅĐĩ видĐļĐĩŅ‚Ņ‹ + 2 ĐŊавĐĩŅ€Ņ…Ņƒ, 1 вĐŊиСŅƒ + КоĐģŅŒĐĩ (ŅˆĐŊŅƒŅ€ĐžĐē ĐŊĐ° ŅˆĐĩĐĩ) + ПŅ€ĐžĐŗŅ€ĐĩŅŅ ŅĐĩĐŗОдĐŊŅ + ПŅ€ĐžĐŗŅ€ĐĩŅŅ Са 7 Đ´ĐŊĐĩĐš + НĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ - ВŅ‹ĐēĐģ. + НоŅ€ĐŧĐ°ĐģŅŒĐŊŅ‹Đš / БĐĩССвŅƒŅ‡ĐŊŅ‹Đš + БоŅ€ŅŒĐąĐ° + ColaCao 2023 + ColaCao 2021 + АĐēŅ‚ивиŅ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅĐēŅ€Đ°ĐŊ Ņ…ĐģĐžĐŋĐēĐžĐŧ Ņ€ŅƒĐēĐ°Đŧи" + ПовŅ‚ĐžŅ€ĐŊŅ‹Đš Ņ…ĐģĐžĐŋĐžĐē ĐŋĐžĐŗĐ°ŅĐ¸Ņ‚ ĐĩĐŗĐž" + Đ­ĐēŅ€Đ°ĐŊ ĐŋĐžĐŗĐ°ŅĐŊĐĩŅ‚ ĐŋĐžŅĐģĐĩ ĐŋŅ€ĐžĐ´ĐžĐģĐļиŅ‚ĐĩĐģŅŒĐŊОК Ņ‚иŅˆĐ¸ĐŊŅ‹, СаĐŧĐĩŅ‡ĐĩĐŊĐŊОК ĐŧиĐēŅ€ĐžŅ„ĐžĐŊĐžĐŧ + Mijia Temperature and Humidity Sensor 2 + ĐŖĐēаСаĐŊиŅ ĐŋĐž ĐŊавиĐŗĐ°Ņ†Đ¸Đ¸ + НаŅŅ‚Ņ€ĐžĐšĐēи вŅŅ‚Ņ€ĐžĐĩĐŊĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиŅ ĐŊавиĐŗĐ°Ņ†Đ¸Đ¸ + ДоĐģĐļĐŊĐž Đģи ĐŋĐžŅĐ˛ĐģŅŅ‚ŅŒŅŅ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐŊавиĐŗĐ°Ņ†Đ¸Đ¸ ĐŋŅ€Đ¸ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊии ĐŊавиĐŗĐ°Ņ†Đ¸ĐžĐŊĐŊŅ‹Ņ… ŅƒĐēаСаĐŊиК + ВибŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ ĐŊОвŅ‹Ņ… ŅƒĐēаСаĐŊиŅŅ… + АвŅ‚ĐžĐŋĐžŅĐ˛ĐģĐĩĐŊиĐĩ + ИĐŧŅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва + ДоĐģĐļĐŊŅ‹ Đģи Ņ‡Đ°ŅŅ‹ вийŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋŅ€Đ¸ ĐŋĐžĐģŅƒŅ‡ĐĩĐŊии иĐģи иСĐŧĐĩĐŊĐĩĐŊии ĐŊавиĐŗĐ°Ņ†Đ¸ĐžĐŊĐŊŅ‹Ņ… ŅƒĐēаСаĐŊиК (ĐēĐžĐŗĐ´Đ° ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐŋĐžĐēаСаĐŊĐž) + Redmi Watch 2 Lite + ĐĄĐžŅŅ‚ĐžŅĐŊиĐĩ ŅĐžĐĩдиĐŊĐĩĐŊиŅ + Redmi Smart Band Pro + Nothing Ear (2) + Nothing Ear (Stick) + ЛŅ‘ĐŗĐēĐžĐĩ Đ°ĐēŅ‚ивĐŊĐžĐĩ ĐŋОдавĐģĐĩĐŊиĐĩ ŅˆŅƒĐŧĐ° + ПŅ€ĐžĐˇŅ€Đ°Ņ‡ĐŊĐžŅŅ‚ŅŒ + Honor Band 5 + Huawei Watch GT + Huawei Band 4 Pro + Huawei Watch GT 2 Pro + Huawei Watch GT 2e + Huawei Talk Band B6 + Huawei Watch GT 3 Pro + ĐŖвĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ ĐŊĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вĐĩ ĐŋŅ€Đ¸ ĐžŅ‚ŅĐžĐĩдиĐŊĐĩĐŊии ĐžŅ‚ Bluetooth. + ĐĸĐžĐģŅŒĐēĐž ĐĩŅĐģи вĐēĐģŅŽŅ‡ĐĩĐŊĐ° Đ°ĐēŅ‚иваŅ†Đ¸Ņ ĐŋĐž ĐŋОдĐŊŅŅ‚иŅŽ Ņ€ŅƒĐēи + \"НĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ\", ĐēĐžĐŗĐ´Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО ŅĐŊŅŅ‚Đž + ВŅ€ŅƒŅ‡ĐŊŅƒŅŽ + Honor Band 4 + Honor Band 6 + Honor Band 7 + Huawei Band (AW70) + ВĐĩŅŅŒ Đ´ĐĩĐŊŅŒ + Honor Band 3 + Huawei Band 8 + Huawei Band 6 + Huawei Band 7 + Đ ĐĩĐļиĐŧ Ņ€Đ°ĐąĐžŅ‚Ņ‹ + НĐĩ ĐžŅ‚ĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ \"ŅƒĐŧĐŊŅ‹Đš ĐŋОдŅŠŅ‘Đŧ\". + НĐĩ вĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒ \"ŅƒĐŧĐŊŅ‹Đš ĐŋОдŅŠŅ‘Đŧ\". + HUAWEI TruSleep â„ĸ + ОŅ‚ŅĐģĐĩĐļиваĐŊиĐĩ ĐēĐ°Ņ‡ĐĩŅŅ‚ва ŅĐŊĐ° и Ņ…Đ°Ņ€Đ°ĐēŅ‚ĐĩŅ€Đ° Đ´Ņ‹Ņ…Đ°ĐŊиŅ в Ņ€ĐĩĐ°ĐģŅŒĐŊĐžĐŧ вŅ€ĐĩĐŧĐĩĐŊи. +\nАĐŊĐ°ĐģиС СаĐēĐžĐŊĐžĐŧĐĩŅ€ĐŊĐžŅŅ‚ĐĩĐš ŅĐŊĐ° и Ņ‚ĐžŅ‡ĐŊŅ‹Đš диаĐŗĐŊОС 6 Ņ‚иĐŋОв ĐŋŅ€ĐžĐąĐģĐĩĐŧ ŅĐž ŅĐŊĐžĐŧ. + ПĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Ņ‚ĐžĐģŅŒĐēĐž Đē ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ‹Đŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ваĐŧ + Ņ€Đ°ŅĐŋОСĐŊаваŅ‚ŅŒ ĐĩСдŅƒ ĐŊĐ° вĐĩĐģĐžŅĐ¸ĐŋĐĩĐ´Đĩ + Ņ€Đ°ŅĐŋОСĐŊаваŅ‚ŅŒ Ņ…ОдŅŒĐąŅƒ + ŅĐŋŅ€Đ°ŅˆĐ¸Đ˛Đ°Ņ‚ŅŒ + авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēи + ПŅ€Đ¸ĐŊŅŅ‚иĐĩ Ņ‚ĐĩĐģ. вŅ‹ĐˇĐžĐ˛ĐžĐ˛ + ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŋŅ€Đ¸ĐŊŅŅ‚иĐĩ вŅ‹ĐˇĐžĐ˛ĐžĐ˛ Ņ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва + ОŅ‚ĐēĐģĐžĐŊĐĩĐŊиĐĩ Ņ‚ĐĩĐģ. вŅ‹ĐˇĐžĐ˛ĐžĐ˛ + АвŅ‚ОиСĐŧĐĩŅ€ĐĩĐŊиĐĩ ŅĐĩŅ€Đ´Ņ†ĐĩйиĐĩĐŊиŅ + АвŅ‚ОиСĐŧĐĩŅ€ĐĩĐŊиĐĩ ĐēиŅĐģĐžŅ€ĐžĐ´Đ° в ĐēŅ€ĐžĐ˛Đ¸ + ФОŅ€ŅĐ¸Ņ€ĐžĐ˛Đ°ĐŊиĐĩ ĐžĐŋŅ†Đ¸Đš + ФОŅ€ŅĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅƒĐŧĐŊŅ‹Đš ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐē + ФОŅ€ŅĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋОддĐĩŅ€ĐļĐēŅƒ ŅƒĐŧĐŊŅ‹Ņ… ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐēОв. +\nИСПОЛĐŦЗĐŖЙĐĸЕ НА СВОЙ РИСК + ФОŅ€ŅĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŧĐĩŅŅ‚Đž ĐŊĐžŅˆĐĩĐŊиŅ + ФОŅ€ŅĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋОддĐĩŅ€ĐļĐēŅƒ Ņ€Đ°ŅĐŋĐžĐģĐžĐļĐĩĐŊиŅ ĐŊĐžŅˆĐĩĐŊиŅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва. +\nИСПОЛĐŦЗĐŖЙĐĸЕ НА СВОЙ РИСК + ФОŅ€ŅĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋОддĐĩŅ€ĐļĐēŅƒ \"ĐŊĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ\" + ИĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ ĐŊĐ°Ņ‡Đ°ĐģĐ° вŅŅ‚аваĐŊиŅ + МоĐļĐĩŅ‚ ĐŋĐžĐŧĐžŅ‡ŅŒ иŅĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ Ņ€Đ°ŅĐŋОСĐŊĐ°ĐŊиĐĩ ŅĐŊĐ°. ĐĄŅ€Đ°ĐˇŅƒ видиĐŧĐž в ОйСОŅ€Đĩ Đ´ĐŊĐĩвĐŊŅ‹Ņ… Đ°ĐēŅ‚ивĐŊĐžŅŅ‚ĐĩĐš. + ИĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅŅ‚Đ°Ņ‚ŅƒŅ ĐžĐēĐžĐŊŅ‡Đ°ĐŊиŅ вŅŅ‚аваĐŊиŅ + ПовŅ‚ĐžŅ€ĐŊŅ‹Đš Đ°ĐŊĐ°ĐģиС Đ´Đ°ĐŊĐŊŅ‹Ņ… Ņ‚Ņ€ĐĩĐŊиŅ€ĐžĐ˛Đēи + ОŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ СаĐŋŅ€ĐžŅ ĐžŅ‚ĐģĐ°Đ´Đēи ĐŊĐ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО Huawei + ЗаĐŋŅ€ĐžŅ ĐžŅ‚ĐģĐ°Đ´Đēи + ĐŖĐģŅƒŅ‡ŅˆĐĩĐŊĐŊĐžĐĩ ĐžŅ‚ŅĐģĐĩĐļиваĐŊиĐĩ ŅĐŊĐ° + ĐŊĐĩŅ‚ + НаŅŅ‚Ņ€ĐžĐšĐēи Ņ€Đ°ŅĐŋОСĐŊĐ°ĐŊиŅ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + Ņ€Đ°ŅĐŋОСĐŊаваŅ‚ŅŒ ĐąĐĩĐŗ + Ņ€Đ°ŅĐŋОСĐŊаваŅ‚ŅŒ ĐŗŅ€ĐĩĐąĐģŅŽ + ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐžŅ‚ĐēĐģĐžĐŊĐĩĐŊиĐĩ вŅ‹ĐˇĐžĐ˛ĐžĐ˛ Ņ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва + ЗаĐŋŅ€ĐĩŅ‚иŅ‚ŅŒ \"ĐŊĐ°ĐšŅ‚и ĐŧОК Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊ\" ĐŋŅ€Đ¸ вĐēĐģŅŽŅ‡ĐĩĐŊĐŊĐžĐŧ Ņ€ĐĩĐļиĐŧĐĩ \"ĐŊĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ\" + НĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Đĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ва ĐģĐžĐļĐŊĐž ĐŋĐžĐŧĐĩŅ‡Đ°ŅŽŅ‚ ŅĐĩĐąŅ ĐēĐ°Đē ĐŊĐĩ иĐŧĐĩŅŽŅ‰Đ¸Đĩ ĐŋОддĐĩŅ€ĐļĐēи ŅŅ‚иŅ… ĐžĐŋŅ†Đ¸Đš. В ŅŅ‚иŅ… ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ°Ņ… иŅ… ĐŧĐžĐļĐŊĐž ĐŋĐžĐŋŅ‹Ņ‚Đ°Ņ‚ŅŒŅŅ вĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ. +\nИСПОЛĐŦЗĐŖЙĐĸЕ НА СВОЙ РИСК +\nЗа ĐŋĐžĐŧĐžŅ‰ŅŒŅŽ ОйŅ€Đ°Ņ‰Đ°ĐšŅ‚ĐĩŅŅŒ Đē ВиĐēи + ФОŅ€ŅĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ĐŋОддĐĩŅ€ĐļĐēŅƒ Ņ€ĐĩĐļиĐŧĐ° \"ĐŊĐĩ ĐąĐĩŅĐŋĐžĐēОиŅ‚ŅŒ\". +\nИСПОЛĐŦЗĐŖЙĐĸЕ НА СВОЙ РИСК + МоĐļĐĩŅ‚ ĐŋĐžĐŧĐžŅ‡ŅŒ иŅĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ Ņ€Đ°ŅĐŋОСĐŊĐ°ĐŊиĐĩ ŅĐŊĐ°. ĐĄŅ€Đ°ĐˇŅƒ видиĐŧĐž в ОйСОŅ€Đĩ Đ´ĐŊĐĩвĐŊŅ‹Ņ… Đ°ĐēŅ‚ивĐŊĐžŅŅ‚ĐĩĐš. + Đ­Ņ‚Đ° ĐžĐŋŅ†Đ¸Ņ ĐŋОвĐģиŅĐĩŅ‚ ĐŊĐ° Ņ‡Ņ‚Đž-Ņ‚Đž ĐŋĐžŅĐģĐĩ ĐŊĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… ОйĐŊОвĐģĐĩĐŊиК + ПĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡Đ°Ņ‚ŅŒŅŅ Ņ‚ĐžĐģŅŒĐēĐž Đē ĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊĐŊŅ‹Đŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚ваĐŧ, Đ° ĐŊĐĩ ĐēĐž вŅĐĩĐŧ ĐŋОдŅ€ŅĐ´ + ЗаŅ€ŅĐ´ йаŅ‚Đ°Ņ€Đĩи ŅĐģиŅˆĐēĐžĐŧ ĐŊиСОĐē + МоŅ‰ĐŊĐžŅŅ‚ŅŒ СвŅƒĐēОиСĐģŅƒŅ‡Đ°Ņ‚ĐĩĐģŅ + ПаŅ€ĐžĐģŅŒ Đ´ĐžĐģĐļĐĩĐŊ ŅĐžŅŅ‚ĐžŅŅ‚ŅŒ иС 4 Ņ†Đ¸Ņ„Ņ€ + Xiaomi Watch S1 Pro + ĐŖŅŅ‚Đ°ĐŊОвĐēĐ° Ņ†Đ¸Ņ„ĐĩŅ€ĐąĐģĐ°Ņ‚Đ° СавĐĩŅ€ŅˆĐĩĐŊĐ° + ĐŖŅŅ‚Đ°ĐŊОвĐēĐ° Ņ†Đ¸Ņ„ĐĩŅ€ĐąĐģĐ°Ņ‚Đ° ĐŊĐĩ ŅƒĐ´Đ°ĐģĐ°ŅŅŒ + ПŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģŅŒĐŊĐ°Ņ ŅƒŅŅ‚Đ°ĐŊОвĐēĐ° Ņ‚иĐŋĐ° ŅĐžĐĩдиĐŊĐĩĐŊиŅ + АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐĩŅĐēиК + Bluetooth LE (ĐŊиСĐēĐžĐĩ ŅĐŊĐĩŅ€ĐŗĐžĐŋĐžŅ‚Ņ€ĐĩĐąĐģĐĩĐŊиĐĩ) + Bluetooth Classic + ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Ой Đ°ĐēŅ‚ивĐŊĐžŅŅ‚и + ВĐĩŅ€ŅĐ¸Ņ 1 + ВĐĩŅ€ŅĐ¸Ņ 3 + ВĐĩŅ€ŅĐ¸Ņ ĐŋŅ€ĐžŅ‚ĐžĐēĐžĐģĐ° + ВĐĩŅ€ŅĐ¸Ņ 2 + ПĐģĐžŅĐēĐžĐĩ Ņ€Đ°ŅŅŅ‚ĐžŅĐŊиĐĩ + Xiaomi Watch S1 + Xiaomi Watch S3 + ЗаĐŗŅ€ŅƒĐˇĐēĐ° Ņ†Đ¸Ņ„ĐĩŅ€ĐąĐģĐ°Ņ‚Đ°â€Ļ + ЗаĐŗŅ€ŅƒĐˇĐēĐ° Ņ†Đ¸Ņ„ĐĩŅ€ĐąĐģĐ°Ņ‚Đ° + ВозĐŧĐžĐļĐŊĐž, ваĐŧ ŅƒĐ´Đ°Ņ‚ŅŒŅŅ СаŅŅ‚авиŅ‚ŅŒ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚вО Ņ€Đ°ĐąĐžŅ‚Đ°Ņ‚ŅŒ Ņ Gadgetbridge, ĐŋŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģŅŒĐŊĐž вŅ‹ŅŅ‚авив ĐŊŅƒĐļĐŊŅ‹Đš Ņ‚иĐŋ ŅĐžĐĩдиĐŊĐĩĐŊиŅ + Xiaomi Smart Band 8 Pro + Mijia MHO-C303 + Sony WI-SP600N + 155 ŅƒĐ´/Đŧ + 165 ŅƒĐ´/Đŧ + ПоŅ€ĐžĐŗ ĐŋŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´ĐĩĐŊиŅ Đž ĐŋОвŅ‹ŅˆĐĩĐŊĐŊĐžĐŧ ŅĐĩŅ€Đ´Ņ†ĐĩйиĐĩĐŊии + ФиŅ‚ĐŊĐĩŅ-ŅƒĐŋŅ€Đ°ĐļĐŊĐĩĐŊиŅ + ФиСиŅ‡ĐĩŅĐēĐ°Ņ ĐŋОдĐŗĐžŅ‚ОвĐēĐ° + ĐĸŅ…ŅĐēвОĐŊĐ´Đž + ФĐĩŅ…Ņ‚ОваĐŊиĐĩ + КŅĐŊĐ´Đž + ПодŅŠŅ‘Đŧ Ņ‚ŅƒĐģОвиŅ‰Đ° + ПодŅ‚ŅĐŗиваĐŊиŅ + ПĐģĐ°ĐŊĐēĐ° + КоĐŋŅŒĐĩ + ПŅ€Ņ‹ĐļĐēи в Đ´ĐģиĐŊŅƒ + ĐĸŅ€Đ°ĐŧĐŋĐģиĐŊ + БаĐģĐĩŅ‚ + БоĐĩвŅ‹Đĩ иŅĐēŅƒŅŅŅ‚ва + ĐĸĐ°Đš-Ņ‡Đ¸ + 175 ŅƒĐ´/Đŧ + 185 ŅƒĐ´/Đŧ + 195 ŅƒĐ´/Đŧ + 205 ŅƒĐ´/Đŧ + ВŅ‹ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŊĐ°ĐŋĐžĐŧиĐŊĐ°ĐŊиŅ Ой ŅƒĐŋĐžŅ‚Ņ€ĐĩĐąĐģĐĩĐŊии вОдŅ‹ ĐŊĐ° вŅ€ĐĩĐŧĐĩĐŊĐŊОК ĐŋŅ€ĐžĐŧĐĩĐļŅƒŅ‚ĐžĐē + БĐĩĐŗ в ĐŋĐžĐŧĐĩŅ‰ĐĩĐŊии + КаŅ€Đ°Ņ‚Đĩ + ОŅ‚ĐļиĐŧĐ°ĐŊиŅ + ПŅ€Ņ‹ĐļĐēи в вŅ‹ŅĐžŅ‚Ņƒ + ОбŅ€ŅƒŅ‡ + CMF Watch Pro + ОŅ…ĐžŅ‚Đ° + Đ Ņ‹ĐąĐ°ĐģĐēĐ° + Redmi Watch 2 + КаŅ€Ņ‚иĐŊĐŗ + БиĐģŅŒŅŅ€Đ´ + ВŅ‹ŅˆĐ¸ĐąĐ°ĐģŅ‹ + ĐĄĐžŅ„Ņ‚йОĐģ + ПиĐēĐģйОĐģ \ No newline at end of file diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index cc63c9150..0141bad93 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -54,7 +54,7 @@ Aktivera lÃĨg vibrationsintensitet pÃĨ armbandet Energisparläget stänger av periodisk automatisk mätning av hjärtfrekvensen, vilket Ãļkar arbetstiden Smart alarmintervall är intervall fÃļre installerat larm. I detta intervall fÃļrsÃļker enheten att upptäcka den lättaste fasen av sÃļmn fÃļr att vakna användare - + Om Gadgetbridge Om Gadgetbridge Nightly Bangle.js kÃļrs @@ -271,4 +271,100 @@ \nDitt band kommer att starta om efter installation av .zip-filen. \n \nFORTSÄTT PÅ EGEN RISK! + Anslut MINST EN enhet som du vill skicka filen till. + Standard + Tid + Gadgetbridge (Nightly, Ingen Pebble-leverantÃļr) + Återanslut automatiskt + Ikonen i statusfältet och pÃĨ lÃĨsskärmen visas + Telefonsamtal + Synkronisera tid + MÃļrk + Aktivera om enhetens mediakontroll inte fungerar fÃļr vissa appar + Upprepningar + Gadgetbridge Nightly + Tema + Ingen LED + Start + Filinstallerare + FÃļr att kunna ta emot bluetooth-samtal mÃĨste du para ihop din telefon med en andra instans av klockan. + Kalender + Klicka här fÃļr att starta ihopparningsprocessen + Svartlista kalendrar + Anslut ENDAST EN enhet som du vill skicka filen till. + Eftersom vi inte alltid kan distribuera mjukvarufilerna mÃĨste du skaffa filerna själv. Det betyder att du kommer att behÃļva sÃļka efter filerna i apk-filer, online, pÃĨ forum, pÃĨ Amazfitwatchfaces (fÃļr Miband/Amazfit-enheter) och sÃĨ vidare. + System + FÃļrdrÃļjning av samtalsaviseringar + Visa telefonnummer eller namn fÃļr inkommande samtal + 1. Tryck pÃĨ knappen nedan fÃļr att starta ihopparningsprocessen. + Gadgetbridge Nightly Utan Pebble + FÃļrfyllda meddelanden + Bangle.js Gadgetbridge + Ljus + VARNING: Om du aktiverar bluetooth-samtal utan att para ihop med den andra instansen kanske samtalsaviseringar inte fungerar som fÃļrväntat. + Dynamiska färger + FÃļredragen Audioplayer + Varning + Synkronisera tid till Gadgetbridge-enhet vid anslutning, och när tid eller tidszon ändras pÃĨ Android-enheten + 3. Aktivera inställningen fÃļr Bluetooth-samtal nedan. + Annat + GPS + Para ihop fÃļr bluetooth-samtal + Broadcast Media knappen uppstÃĨr direkt + Använd svart bakgrund i mÃļrkt tema + Träning + Hämtar firmware/app filen + Equalizer + Du är pÃĨ väg att installera mjukvaran %s pÃĨ din Amazfit Bip 3 Pro. +\n +\nSe till att installera .fw-filen och efter det .res-filen. Din klocka kommer att starta om efter installationen av .fw-filen. +\n +\nObs: Du behÃļver inte installera .res om exakt samma var installerad tidigare. +\n +\nFORTSÄTT PÅ EGEN RISK! + Ljud & Vibration + Hälsa + Bluetooth-samtalsinställningar + Anslutning + Bluetooth-samtalsparning + Allmänna Inställningar + UtfÃļr och ta emot samtal direkt pÃĨ klockan + Aviseringar + DÃļlj Gadgetbridge-avisering + Du är pÃĨ väg att installera mjukvaran %s pÃĨ din Amazfit Cheetah Pro. +\n +\nDitt armband kommer att starta om efter installationen av .zip-filen. +\n +\nFORTSÄTT PÅ EGEN RISK! + Svartlistade kalendrar synkroniseras inte med enheten + System + Inställningar + Välj fil + Bangle.js Gadgetbridge (Nightly) + FÃļrdrÃļj innan aviseringar skickas om inkommande samtal till enheten, i sekunder. + Denna funktion kan potentiellt orsaka att din enhet inte kan starta. Med det sagt, detta har aldrig hänt nÃĨgon av utvecklarna vid flashning, kom ihÃĨg att detta är pÃĨ egen risk. + Bangle.js Gadgetbridge + Du är pÃĨ väg att installera mjukvaran %s pÃĨ din %s. +\n +\nDitt klocka kommer att starta om efter installationen av .zip-filen. +\n +\nFORTSÄTT PÅ EGEN RISK! + Anslut till Gadgetbridge-enheten när Bluetooth aktiveras + Starta automatiskt + SprÃĨk + Välj filen som ska ÃļverfÃļras till enheten: %s + Ikonen i statusfältet och pÃĨ lÃĨsskärmen är dÃļljd + Visa kontaktinformation + GPS + GNOLASS + Ändringslogg + 2. GÃĨ till telefonens bluetooth-inställningar och koppla ihop med den nya enheten som dyker upp (liknande namn pÃĨ din nuvarande klocka, men med ett suffix, t.ex. \"Amazfit GTR 4 - AFC8\". + Se till att enheten %s är ansluten + Bluetooth-samtal + Skärm + Hur man tar emot bluetooth-samtal + GPS + BDS + Bangle.js Gadgetbridge Nightly + Gadgetbridge (Nightly) + Datum och Tid + GPS + GALILEO \ No newline at end of file diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml new file mode 100644 index 000000000..495d44502 --- /dev/null +++ b/app/src/main/res/values-th/strings.xml @@ -0,0 +1,4 @@ + + + āš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸š Gadgetbridge + \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 9fd87acab..727c2c18e 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -2177,4 +2177,15 @@ Bluetooth aramalarÄą nasÄąl alÄąnÄąr Bluetooth aramalarÄą Eşleştirme işlemini başlatmak için buraya tÄąklayÄąn + FransÄązca (Kanada) + Amazfit Bip 3 Pro cihazÄąnÄąza %s yazÄąlÄąm dosyasÄąnÄą yÃŧklemek Ãŧzeresiniz. +\n +\nLÃŧtfen Ãļnce .fw dosyasÄąnÄą, ardÄąndan .res dosyasÄąnÄą yÃŧklediğinizden emin olun. Saatiniz .fw dosyasÄąnÄą yÃŧkledikten sonra yeniden başlayacaktÄąr. +\n +\nNot: Eğer aynÄą .res dosyasÄą zaten yÃŧklÃŧ ise bir daha yÃŧklemenize gerek yoktur. +\n +\nÄ°LERLEMENÄ°Z DURUMUNDA RÄ°SK SÄ°ZE AÄ°TTÄ°R! + FransÄązca (Fransa) + Dosya yÃŧklenemedi, aygÄąt desteklenmiyor. + Yenilikler \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 4308af82d..9b8622d3a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -42,7 +42,7 @@ АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐĩ ĐŋĐĩŅ€ĐĩС\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ БаĐļĐ°ĐŊиК ĐŧŅƒĐˇĐ¸Ņ‡ĐŊиК ĐŋŅ€ĐžĐŗŅ€Đ°Đ˛Đ°Ņ‡ ĐĸиĐŋОвиК - ДаŅ‚Đ° Ņ‚Đ° Ņ‡Đ°Ņ + ДаŅ‚Đ° Đš Ņ‡Đ°Ņ ХиĐŊŅ…Ņ€ĐžĐŊŅ–СаŅ†Ņ–Ņ Ņ‡Đ°ŅŅƒ ХиĐŊŅ…Ņ€ĐžĐŊŅ–СŅƒĐ˛Đ°Ņ‚и Ņ‡Đ°Ņ ĐŋŅ–Đ´ Ņ‡Đ°Ņ С\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ С Gadgetbridge, Đ° Ņ‚Đ°ĐēĐžĐļ ĐŋŅ–Đ´ Ņ‡Đ°Ņ СĐŧŅ–ĐŊи Ņ‡Đ°ŅŅƒ айО Ņ‡Đ°ŅĐžĐ˛ĐžĐŗĐž ĐŋĐžŅŅŅƒ в ŅĐ¸ŅŅ‚ĐĩĐŧŅ– Android ĐĸĐĩĐŧĐ° @@ -75,7 +75,7 @@ ДодаŅ‚и ĐĩĐēŅĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģŅŒĐŊŅƒ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧĐēŅƒ СаŅŅ‚ĐžŅŅƒĐŊĐēŅ–в Android, ŅĐēŅ– виĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒŅŽŅ‚ŅŒ PebbleKit ĐĄŅ…Ņ–Đ´ Ņ– СаŅ…Ņ–Đ´ ŅĐžĐŊŅ†Ņ На ĐžŅĐŊОвŅ– Ņ€ĐžĐˇŅ‚Đ°ŅˆŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚и Ņ‡Đ°Ņ ŅŅ…ОдŅƒ Ņ– СаŅ…ОдŅƒ ŅĐžĐŊŅ†Ņ Đ´Đž Ņ€ĐžĐˇĐēĐģĐ°Đ´Ņƒ Pebble - МŅ–ŅŅ†ĐĩĐŋĐĩŅ€ĐĩĐąŅƒĐ˛Đ°ĐŊĐŊŅ + РОСŅ‚Đ°ŅˆŅƒĐ˛Đ°ĐŊĐŊŅ ШиŅ€ĐžŅ‚Đ° ДовĐŗĐžŅ‚Đ° ПŅ€Đ¸ĐŧŅƒŅĐžĐ˛Đ¸Đš ĐŋŅ€ĐžŅ‚ĐžĐēĐžĐģ ŅĐŋОвŅ–Ņ‰ĐĩĐŊŅŒ @@ -173,7 +173,7 @@ БŅƒĐ´Đ¸ĐģŅŒĐŊиĐē ĐŊĐ°Đ´Ņ–ŅĐģĐ°ĐŊĐž ĐŊĐ° ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš. ДаĐŊŅ– вŅ–Đ´ŅŅƒŅ‚ĐŊŅ–. ХиĐŊŅ…Ņ€ĐžĐŊŅ–СŅƒĐ˛Đ°Ņ‚и ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš? БŅƒĐ´Đĩ ĐŋĐĩŅ€ĐĩĐ´Đ°ĐŊĐž %1$s Đ´Đ°ĐŊиŅ…, ĐŋĐžŅ‡Đ¸ĐŊĐ°ŅŽŅ‡Đ¸ С %2$s - ЊОдĐĩĐŊĐŊĐ° Ņ†Ņ–ĐģŅŒ ĐēŅ€ĐžĐēŅ–в + ЊОдĐĩĐŊĐŊĐ° ĐŧĐĩŅ‚Đ° ĐēŅ€ĐžĐēŅ–в ПоĐŧиĐģĐēĐ° виĐēĐžĐŊĐ°ĐŊĐŊŅ \'%1$s\' ВаŅˆĐ° Đ°ĐēŅ‚ивĐŊŅ–ŅŅ‚ŅŒ НĐĩĐŧĐžĐļĐģивО С\'Ņ”Đ´ĐŊĐ°Ņ‚иŅŅ: %1$s @@ -258,27 +258,27 @@ ЗабĐģĐžĐēŅƒĐ˛Đ°Ņ‚и вŅŅ– ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ДозвоĐģиŅ‚и вŅŅ– ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ЧОŅ€ĐŊиК ŅĐŋиŅĐžĐē ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ–в - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Bip. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Bip. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ — .res, Đ° Đ´Đ°ĐģŅ– — .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ .res, Đ° в ĐēŅ–ĐŊŅ†Ņ– .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°. ВаĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēŅ– Đļ, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚Đ°ĐēŅ– Đļ ŅĐ°ĐŧŅ–, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Cor. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Cor. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! - Ви ĐŧĐ°Ņ”Ņ‚Đĩ ĐŊĐ°ĐŧŅ–Ņ€ вŅŅ‚Đ°ĐŊОвиŅ‚и %s ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ ĐŊĐ° ваŅˆ Mi Band 3. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Mi Band 3. \n -\nБŅƒĐ´ŅŒ ĐģĐ°ŅĐēĐ°, ĐŊĐĩ СайŅƒĐ´ŅŒŅ‚Đĩ вŅŅ‚Đ°ĐŊОвиŅ‚и Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋŅ–ŅĐģŅ ĐŊŅŒĐžĐŗĐž – .res Ņ„Đ°ĐšĐģ. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐąŅƒĐ´Đĩ ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŋŅ–ŅĐģŅ ŅƒŅŅ‚Đ°ĐŊОвĐēи Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°. ВаĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК ĐļĐĩ, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n -\nВИ ДІЄĐĸЕ НА ВЛАСНИЙ РИЗИК! +\nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! ĐŖвŅ–ĐŧĐēĐŊŅƒŅ‚и йОĐēОвŅ– Ņ€ŅƒŅ…и Ņƒ Đ´Ņ–Đ°ĐŗŅ€Đ°ĐŧĐ°Ņ… Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– ĐĄŅ…ОваŅ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Gadgetbridge ПŅ–ĐēŅ‚ĐžĐŗŅ€Đ°ĐŧĐ° в Ņ€ŅĐ´ĐēŅƒ ŅŅ‚Đ°ĐŊŅƒ Ņ‚Đ° ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŊĐ° ĐĩĐēŅ€Đ°ĐŊŅ– ĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ вŅ–дОйŅ€Đ°ĐļĐ°ŅŽŅ‚ŅŒŅŅ @@ -289,9 +289,9 @@ ĐŖвŅ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ†ĐĩĐš ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€, ŅĐēŅ‰Đž ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ” ŅˆŅ€Đ¸Ņ„Ņ‚ ваŅˆĐžŅ— ĐŧОви Đ ĐĩĐļиĐŧ ĐŋŅ€Đ¸Đ˛Đ°Ņ‚ĐŊĐžŅŅ‚Ņ– виĐēĐģиĐēŅ–в ПоĐēаСŅƒĐ˛Đ°Ņ‚и Ņ–Đŧ\'Ņ Ņ‚Đ° ĐŊĐžĐŧĐĩŅ€ - ПŅ€Đ¸Ņ…ОваŅ‚и Ņ–Đŧ\'Ņ, Đ°ĐģĐĩ ĐŋĐžĐēаСаŅ‚и ĐŊĐžĐŧĐĩŅ€ - ПŅ€Đ¸Ņ…ОваŅ‚и ĐŊĐžĐŧĐĩŅ€, Đ°ĐģĐĩ ĐŋĐžĐēаСаŅ‚и Ņ–Đŧ\'Ņ - ПŅ€Đ¸Ņ…ОваŅ‚и Ņ–Đŧ\'Ņ Ņ‚Đ° ĐŊĐžĐŧĐĩŅ€ + ПŅ€Đ¸Ņ…ОвŅƒĐ˛Đ°Ņ‚и Ņ–Đŧ\'Ņ, Đ°ĐģĐĩ ĐŋĐžĐēаСŅƒĐ˛Đ°Ņ‚и ĐŊĐžĐŧĐĩŅ€ + ПŅ€Đ¸Ņ…ОвŅƒĐ˛Đ°Ņ‚и ĐŊĐžĐŧĐĩŅ€, Đ°ĐģĐĩ ĐŋĐžĐēаСŅƒĐ˛Đ°Ņ‚и Ņ–Đŧ\'Ņ + ПŅ€Đ¸Ņ…ОвŅƒĐ˛Đ°Ņ‚и Ņ–Đŧ\'Ņ Ņ‚Đ° ĐŊĐžĐŧĐĩŅ€ ПоĐŗОда РОСŅ‚Đ°ŅˆŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋĐžĐŗОди (LineageOS) ЗабĐģĐžĐēОваĐŊŅ– ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ– @@ -355,7 +355,7 @@ ХиĐŗĐŊĐ°Đģи Đ´ĐģŅ Ņ€ĐĩСĐĩŅ€Đ˛ŅƒĐ˛Đ°ĐŊĐŊŅ Đ´ĐģŅ ĐŧаКйŅƒŅ‚ĐŊŅ–Ņ… ĐŋОдŅ–Đš ВиĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒĐ˛Đ°Ņ‚и Đ´Đ°Ņ‚Ņ‡Đ¸Đē ĐŋŅƒĐģŅŒŅŅƒ Đ´ĐģŅ ĐŋĐžĐģŅ–ĐŋŅˆĐĩĐŊĐŊŅ виŅĐ˛ĐģĐĩĐŊĐŊŅ ŅĐŊŅƒ ЧаŅ - ДаŅ‚Đ° Ņ‚Đ° Ņ‡Đ°Ņ + ДаŅ‚Đ° Đš Ņ‡Đ°Ņ ОдиĐŊиŅ†Ņ– виĐŧŅ–Ņ€ŅŽĐ˛Đ°ĐŊĐŊŅ ĐĸĐžŅ€ĐēĐŊŅ–Ņ‚ŅŒŅŅ ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ Đ´ĐģŅ вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ— ĐĄŅ‚вОŅ€ĐĩĐŊĐŊŅ ĐŋĐ°Ņ€Đ¸ С %sâ€Ļ @@ -379,8 +379,8 @@ НĐĩ вдаĐģĐžŅŅ видаĐģиŅ‚и йаСŅƒ Đ´Đ°ĐŊиŅ…. ПĐĩŅ€ĐĩСаĐŋиŅĐ°Ņ‚и ГаŅ€Đ°ĐˇĐ´ - МĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊĐ° - ІĐŧĐŋĐĩŅ€ŅŅŒĐēĐ° + МĐĩŅ‚Ņ€Đ¸Ņ‡ĐŊŅ– + ІĐŧĐŋĐĩŅ€ŅŅŒĐēŅ– БŅƒĐ´Đ¸ĐģŅŒĐŊиĐē (%1$s) ЗĐŊаКдĐĩĐŊĐž! @@ -435,11 +435,11 @@ ІŅŅ‚ĐžŅ€Ņ–Ņ Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊŅŒ ПоĐŗОда БŅƒĐ´Đ¸ĐģŅŒĐŊиĐē - ВŅ–Đ´ĐģŅ–Đē + ĐĸĐ°ĐšĐŧĐĩŅ€ КоĐŧĐŋĐ°Ņ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ МŅƒĐˇĐ¸ĐēĐ° - ĐŠĐĩ + ДодаŅ‚ĐēОвО ĐĨвиĐģиĐŊ: ГодиĐŊ: ĐĄĐĩĐēŅƒĐŊĐ´: @@ -491,7 +491,7 @@ ПоĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŊĐĩĐ°ĐēŅ‚ивĐŊŅ–ŅŅ‚ŅŒ ПоŅ€Ņ–Đŗ ĐŊĐĩĐ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– (Ņ…в) ЧаŅ ĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ - ЗавĐĩŅ€ŅˆĐĩĐŊĐŊŅ + ЧаŅ СавĐĩŅ€ŅˆĐĩĐŊĐŊŅ АвŅ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Ņ–Ņ ПоŅ‚Ņ€Ņ–ĐąĐŊĐ° авŅ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Ņ–Ņ БаĐļĐ°ĐŊĐ° Ņ‚Ņ€Đ¸Đ˛Đ°ĐģŅ–ŅŅ‚ŅŒ ŅĐŊŅƒ в ĐŗОдиĐŊĐ°Ņ… @@ -535,7 +535,7 @@ КоŅ€ĐĩĐšŅŅŒĐēĐ° Đ¯ĐŋĐžĐŊŅŅŒĐēĐ° Ви ŅĐŋĐ°Đģи С %1$s Đ´Đž %2$s - НоŅ€Đ˛ĐĩСŅŒĐēиК ĐģŅ–Ņ‚ĐĩŅ€Đ°Ņ‚ŅƒŅ€ĐŊиК + НоŅ€Đ˛ĐĩСŅŒĐēĐ° (БŅƒĐēĐŧĐžĐģ) НŅ–Ņ‡ĐŊиК Ņ€ĐĩĐļиĐŧ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŗŅ€Đ°Ņ„Ņ–ĐēŅ–в НайŅˆĐ˛Đ¸Đ´ŅˆĐĩ ŅĐĩŅ€Ņ†ĐĩйиŅ‚Ņ‚Ņ @@ -611,7 +611,7 @@ ІĐŧĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚и Đ´Đ°ĐŊŅ– КĐŊĐžĐŋĐēĐ° ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ ĐŊОвОĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ ЗавĐļди видĐŊĐž - ВидĐŊĐž ŅĐēŅ‰Đž ĐŊĐĩ дОдаĐŊĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš + ВидĐŊĐž, ŅĐēŅ‰Đž ĐŊĐĩ дОдаĐŊĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Makibes HR3 ЗĐŊĐ°ĐšŅ‚и Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊ ЧĐĩŅ€Đ˛ĐžĐŊиК @@ -625,12 +625,12 @@ ВŅ–Đ´ĐēĐģĐ°ŅŅ‚и ЗŅ€ĐžĐąŅ–Ņ‚ŅŒ ваŅˆ ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš виŅĐ˛ĐŊиĐŧ. НĐĩдавĐŊĐž ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊŅ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—, Ņ–ĐŧОвŅ–Ņ€ĐŊĐž, ĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ŅŒ виŅĐ˛ĐģĐĩĐŊŅ–. АĐēŅ‚ивŅƒĐšŅ‚Đĩ Ņ€ĐžĐˇŅ‚Đ°ŅˆŅƒĐ˛Đ°ĐŊĐŊŅ (GPS) ĐŊĐ° Android 6+. ВиĐŧĐēĐŊŅ–Ņ‚ŅŒ СаŅ…иŅŅ‚ ĐŋŅ€Đ¸Đ˛Đ°Ņ‚ĐŊĐžŅŅ‚Ņ– Đ´ĐģŅ Gadgetbridge, ĐžŅĐēŅ–ĐģŅŒĐēи Ņ†Đĩ ĐŧĐžĐļĐĩ ĐŋŅ€Đ¸ĐˇĐ˛ĐĩŅŅ‚и Đ´Đž СйОŅŽ Ņ– ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊŅƒ. Đ¯ĐēŅ‰Đž Са ĐēŅ–ĐģŅŒĐēĐ° Ņ…виĐģиĐŊ ĐŊĐĩ ĐąŅƒĐ´Đĩ виŅĐ˛ĐģĐĩĐŊĐž ĐļОдĐŊĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ, ĐŋОвŅ‚ĐžŅ€Ņ–Ņ‚ŅŒ ŅĐŋŅ€ĐžĐąŅƒ ĐŋŅ–ŅĐģŅ ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŧОйŅ–ĐģŅŒĐŊĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ. ĐŖвŅ–ĐŧĐēĐŊŅƒŅ‚и Ņ€ĐĩĐļиĐŧ ĐŊиСŅŒĐēĐžŅ— СаŅ‚Ņ€Đ¸ĐŧĐēи Đ´ĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°Đŧи - БŅ€Đ°ŅĐģĐĩŅ‚ СавŅ–ĐąŅ€ŅƒŅ” Са Đ´ĐžŅŅĐŗĐŊĐĩĐŊĐŊŅ Ņ‰ĐžĐ´ĐĩĐŊĐŊĐžŅ— Ņ†Ņ–ĐģŅ– ĐēŅ–ĐģŅŒĐēĐžŅŅ‚Ņ– ĐēŅ€ĐžĐēŅ–в - ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐŋŅƒĐŊĐēŅ‚и ĐŧĐĩĐŊŅŽ Đ´ĐģŅ ĐŋĐžĐēаСŅƒ ĐŊĐ° ĐĩĐēŅ€Đ°ĐŊŅ– ĐŗОдиĐŊĐŊиĐēĐ° + БŅ€Đ°ŅĐģĐĩŅ‚ вŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚иĐŧĐĩ Са Đ´ĐžŅŅĐŗĐŊĐĩĐŊĐŊŅ Ņ‰ĐžĐ´ĐĩĐŊĐŊĐžŅ— Ņ†Ņ–ĐģŅ– ĐēŅ–ĐģŅŒĐēĐžŅŅ‚Ņ– ĐēŅ€ĐžĐēŅ–в + ВибĐĩŅ€Ņ–Ņ‚ŅŒ ĐŋŅƒĐŊĐēŅ‚и ĐŧĐĩĐŊŅŽ Đ´ĐģŅ ĐŋĐžĐēаСŅƒ ĐŊĐ° ĐĩĐēŅ€Đ°ĐŊŅ– ĐąŅ€Đ°ŅĐģĐĩŅ‚Đ° ОбĐĩŅ€ĐŊŅ–Ņ‚ŅŒ Ņ€ŅƒĐēŅƒ, Ņ‰ĐžĐą СĐŧŅ–ĐŊиŅ‚и вŅ–Đ´ĐžĐŧĐžŅŅ‚Ņ– Đ¯ĐēŅ‰Đž ввŅ–ĐŧĐēĐŊŅƒŅ‚и, ĐŗОдиĐŊĐŊиĐē ĐŊĐĩ ĐžŅ‚Ņ€Đ¸ĐŧŅƒĐ˛Đ°Ņ‚иĐŧĐĩ ŅĐŋОвŅ–Ņ‰ĐĩĐŊŅŒ - БŅ€Đ°ŅĐģĐĩŅ‚ СавŅ–ĐąŅ€ŅƒŅ”, ŅĐēŅ‰Đž ви ĐąŅƒĐ´ĐĩŅ‚Đĩ ĐŊĐĩĐ°ĐēŅ‚ивĐŊиĐŧи Đ´ĐĩŅĐēиК Ņ‡Đ°Ņ - ВиĐŧĐēĐŊŅƒŅ‚и ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŊĐĩĐ°ĐēŅ‚ивĐŊŅ–ŅŅ‚ŅŒ вĐŋŅ€ĐžĐ´ĐžĐ˛Đļ ĐŋĐĩвĐŊĐžĐŗĐž ĐŋŅ€ĐžĐŧŅ–ĐļĐēŅƒ Ņ‡Đ°ŅŅƒ + БŅ€Đ°ŅĐģĐĩŅ‚ вŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚иĐŧĐĩ, ŅĐēŅ‰Đž ви ĐąŅƒĐģи ĐŊĐĩĐ°ĐēŅ‚ивĐŊŅ– ĐŋŅ€ĐžŅ‚ŅĐŗĐžĐŧ ĐŋĐĩвĐŊĐžĐŗĐž Ņ‡Đ°ŅŅƒ + ВиĐŧиĐēĐ°Ņ‚и ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŊĐĩĐ°ĐēŅ‚ивĐŊŅ–ŅŅ‚ŅŒ вĐŋŅ€ĐžĐ´ĐžĐ˛Đļ ĐŋĐĩвĐŊĐžĐŗĐž ĐŋŅ€ĐžĐŧŅ–ĐļĐēŅƒ Ņ‡Đ°ŅŅƒ ГоŅ‚ОвиК Đ´ĐģŅ ĐŋĐĩŅ€ĐĩĐ´Đ°Ņ‡Ņ– Đ´Đ°ĐŊиŅ… вŅ–Đ´ %1$s Zzz ПоĐŧиĐģĐēĐ° ŅŅ‚вОŅ€ĐĩĐŊĐŊŅ Ņ‚ĐĩĐēи Đ´ĐģŅ log-Ņ„Đ°ĐšĐģŅƒ: %1$s @@ -697,8 +697,8 @@ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚и ĐŋĐžŅ‚ĐžŅ‡ĐŊиК Ņ‡Đ°Ņ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ. Watch 9 ŅŅ‚вОŅ€ĐĩĐŊĐŊŅ С\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ Watch 9 ĐēĐ°ĐģŅ–ĐąŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ - РОСйĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ° ĐŗОдиĐŊĐŊиĐēĐ° - ПоŅŅƒĐŊŅŒŅ‚Đĩ вĐŗĐžŅ€Ņƒ Đ´ĐģŅ Ņ€ĐžĐˇĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ° ĐŗОдиĐŊĐŊиĐēĐ° + РОСйĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚Đ° + ПоŅŅƒĐŊŅŒŅ‚Đĩ вĐŗĐžŅ€Ņƒ Đ´ĐģŅ Ņ€ĐžĐˇĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚Đ° АвŅ‚ОСĐŊиĐļĐĩĐŊĐŊŅ ŅŅĐēŅ€Đ°Đ˛ĐžŅŅ‚Ņ– ĐĩĐēŅ€Đ°ĐŊĐ° вĐŊĐžŅ‡Ņ– ПаĐŧ\'ŅŅ‚Đ°ĐšŅ‚Đĩ, Ņ‰Đž Ņ„Đ°ĐšĐģи ĐļŅƒŅ€ĐŊĐ°ĐģŅ–в Gadgetbridge ĐŧĐžĐļŅƒŅ‚ŅŒ ĐŧŅ–ŅŅ‚иŅ‚и йаĐŗĐ°Ņ‚Đž ĐŋĐĩŅ€ŅĐžĐŊĐ°ĐģŅŒĐŊиŅ… Đ´Đ°ĐŊиŅ…, вĐēĐģŅŽŅ‡ĐŊĐž С, Đ°ĐģĐĩ ĐŊĐĩ ОйĐŧĐĩĐļŅƒŅŽŅ‡Đ¸ŅŅŒ, Đ´Đ°ĐŊиĐŧи ĐŋŅ€Đž СдОŅ€ĐžĐ˛\'Ņ, ŅƒĐŊŅ–ĐēĐ°ĐģŅŒĐŊиĐŧи Ņ–Đ´ĐĩĐŊŅ‚иŅ„Ņ–ĐēĐ°Ņ‚ĐžŅ€Đ°Đŧи (ŅĐē-ĐžŅ‚ MAC Đ°Đ´Ņ€ĐĩŅĐ°), ĐŧŅƒĐˇĐ¸Ņ‡ĐŊŅ– вĐŋОдОйаĐŊĐŊŅ Ņ‚ĐžŅ‰Đž. ВŅ–Đ´Ņ€ĐĩĐ´Đ°ĐŗŅƒĐšŅ‚Đĩ Ņ„Đ°ĐšĐģ Ņ‚Đ° виĐģŅƒŅ‡Ņ–Ņ‚ŅŒ Ņ†Ņ– Đ´Đ°ĐŊŅ– ĐŋĐĩŅ€ĐĩĐ´ ĐŊĐ°Đ´ŅĐ¸ĐģĐ°ĐŊĐŊŅĐŧ. КоĐŊŅ‚ĐĩĐēŅŅ‚ĐŊĐ° Đ°Ņ€Đ°ĐąŅŅŒĐēĐ° ĐŧОва @@ -718,28 +718,28 @@ ВŅ–ĐąŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ОдиĐŊ Ņ€Đ°Đˇ ВŅ–ĐąŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ двŅ–Ņ‡Ņ– ВŅ–ĐąŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ– СвŅƒĐē ОдиĐŊ Ņ€Đ°Đˇ - ЊОдĐĩĐŊĐŊĐ° Ņ†Ņ–ĐģŅŒ: ŅĐŋĐ°ĐģĐĩĐŊŅ– ĐēĐ°ĐģĐžŅ€Ņ–Ņ— - ЊОдĐĩĐŊĐŊĐ° Ņ†Ņ–ĐģŅŒ: вŅ–Đ´ŅŅ‚Đ°ĐŊŅŒ Ņƒ ĐŧĐĩŅ‚Ņ€Đ°Ņ… - ЊОдĐĩĐŊĐŊĐ° Ņ†Ņ–ĐģŅŒ: Đ°ĐēŅ‚ивĐŊиК Ņ‡Đ°Ņ Ņƒ Ņ…виĐģиĐŊĐ°Ņ… + ЊОдĐĩĐŊĐŊĐ° ĐŧĐĩŅ‚Đ°: ŅĐŋĐ°ĐģĐĩĐŊŅ– ĐēĐ°ĐģĐžŅ€Ņ–Ņ— + ЊОдĐĩĐŊĐŊĐ° ĐŧĐĩŅ‚Đ°: вŅ–Đ´ŅŅ‚Đ°ĐŊŅŒ Ņƒ ĐŧĐĩŅ‚Ņ€Đ°Ņ… + ЊОдĐĩĐŊĐŊĐ° ĐŧĐĩŅ‚Đ°: Đ°ĐēŅ‚ивĐŊиК Ņ‡Đ°Ņ Ņƒ Ņ…виĐģиĐŊĐ°Ņ… Mi Scale 2 ЗĐŧŅ–ĐŊŅ–Ņ‚ŅŒ ĐēĐģŅŽŅ‡ авŅ‚ĐĩĐŊŅ‚иŅ„Ņ–ĐēĐ°Ņ†Ņ–Ņ— ĐŊĐ° СаĐŗĐ°ĐģŅŒĐŊиК ĐēĐģŅŽŅ‡ ĐŊĐ° вŅŅ–Ņ… ваŅˆĐ¸Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŅ… Android, С ŅĐēиĐŧи ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž С\'Ņ”Đ´ĐŊĐ°Ņ‚иŅŅ. ПоĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Đš ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊиК ĐēĐģŅŽŅ‡ Đ´ĐģŅ вŅŅ–Ņ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—в 0123456789@ABCDE BFH-16 - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Cor 2. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Cor 2. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! \n \nПОВНІСĐĸĐŽ НЕ ПРОĐĸЕСĐĸОВАНО, ІМОВІРНО ВАМ ПОĐĸРІБНО ВСĐĸАНОВИĐĸИ МІКРОПРОГРАМĐŖ BEATS_W Đ¯ĐšĐŠĐž ВАШ ПРИСĐĸРІЙ \"Amazfit Band 2\" Amazfit Cor 2 Mi Smart Band 4 - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Mi Smart Band 4. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Mi Band 4. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! ПоĐēаСŅƒĐ˛Đ°Ņ‚и ŅĐĩŅ€ĐĩĐ´ĐŊŅ” СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ в Đ´Ņ–Đ°ĐŗŅ€Đ°ĐŧŅ– @@ -777,27 +777,27 @@ ĐĸŅ€Đ¸Đ˛Đ°ĐģŅ–ŅŅ‚ŅŒ виĐēĐģиĐēŅƒ в ŅĐĩĐēŅƒĐŊĐ´Đ°Ņ… ĐĸŅ€Đ¸Đ˛Đ°ĐģŅ–ŅŅ‚ŅŒ ĐĻĐĩĐš ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒŅ” ĐēĐģŅŽŅ‡Đ° авŅ‚ĐĩĐŊŅ‚иŅ„Ņ–ĐēĐ°Ņ†Ņ–Ņ—, СаŅ‚иŅĐŊŅ–Ņ‚ŅŒ Đ´ĐģŅ ĐŋĐĩŅ€ĐĩŅ…ОдŅƒ в ĐŧĐĩĐŊŅŽ ввĐĩĐ´ĐĩĐŊĐŊŅ ĐēĐģŅŽŅ‡Đ°. ПŅ€ĐžŅ‡Đ¸Ņ‚Đ°ĐšŅ‚Đĩ ВŅ–ĐēŅ–. - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Bip Lite. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Bip Lite. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Amazfit GTR - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTR. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTR. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ — .res, Đ° Đ´Đ°ĐģŅ– — .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ .res, Đ° в ĐēŅ–ĐŊŅ†Ņ– .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°. ВаĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēŅ– Đļ, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚Đ°ĐēŅ– ŅĐ°ĐŧŅ–, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Amazfit GTS - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTS. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTS. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ — .res, Đ° Đ´Đ°ĐģŅ– — .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ .res, Đ° в ĐēŅ–ĐŊŅ†Ņ– .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°. ВаĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēŅ– Đļ, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚Đ°ĐēŅ– ŅĐ°ĐŧŅ–, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Fossil Q Hybrid @@ -846,7 +846,7 @@ ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Ņ– виĐēĐģиĐēи Watch X Plus ĐēĐ°ĐģŅ–ĐąŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ХиĐģĐ° вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ— - ĐŖвŅ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ, ŅĐēŅ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ вŅŅ‚Đ°ĐŊОвиŅ‚и ĐŋŅ€ĐžŅˆĐ¸Đ˛ĐēŅƒ ĐŊĐĩ ĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊŅƒ Đ´ĐģŅ ваŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ (ĐŊĐ° вĐģĐ°ŅĐŊиК Ņ€Đ¸ĐˇĐ¸Đē) + ĐŖвŅ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ, ŅĐēŅ‰Đž ви Ņ…ĐžŅ‡ĐĩŅ‚Đĩ вŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ ĐŊĐĩ ĐŋŅ€Đ¸ĐˇĐŊĐ°Ņ‡ĐĩĐŊŅƒ Đ´ĐģŅ ваŅˆĐžĐŗĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ (ĐŊĐ° вĐģĐ°ŅĐŊиК Ņ€Đ¸ĐˇĐ¸Đē) ВиĐŧĐēĐŊŅƒŅ‚и ĐŋĐĩŅ€ĐĩĐŋŅ€ĐžĐ˛Ņ–Ņ€ĐēŅƒ ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°Đŧи ХиĐŊŅ…Ņ€ĐžĐŊŅ–СŅƒĐ˛Đ°Ņ‚и ĐŋОдŅ–Ņ— ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ ĐŊĐ°Đ´Ņ…ОдиŅ‚иĐŧŅƒŅ‚ŅŒ ĐŊавŅ–Ņ‚ŅŒ ĐēĐžĐģи ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš вŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊĐž @@ -900,7 +900,7 @@ Lemfo SG2 Amazfit T-Rex Mi Band 5 - swolf Ņ–ĐŊĐ´ĐĩĐēŅ + SWOLF ĐĄŅ‚иĐģŅŒ ĐŋĐģаваĐŊĐŊŅ ЗаŅŅ‚ĐžŅĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ Đ´ĐģŅ ĐŋĐžŅŅ‚Đ°Ņ‡Đ°ĐģŅŒĐŊиĐēĐ° ĐŋĐžĐŗОди LineageOS, Ņ–ĐŊŅˆŅ– вĐĩŅ€ŅŅ–Ņ— Android ĐŧĐ°ŅŽŅ‚ŅŒ виĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒĐ˛Đ°Ņ‚и СаŅŅ‚ĐžŅŅƒĐŊĐēи ŅĐē-ĐžŅ‚ \"Weather notification\". ШŅƒĐēĐ°ĐšŅ‚Đĩ ĐąŅ–ĐģŅŒŅˆĐĩ вŅ–Đ´ĐžĐŧĐžŅŅ‚ĐĩĐš Ņƒ вŅ–ĐēŅ– Gadgetbridge. ВиĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒĐ˛Đ°Ņ‚и ĐŋОдŅ–Ņ— ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ Đ´ĐģŅ СаĐŋŅƒŅĐēŅƒ Đ´Ņ–Đš Ņ– Ņ‚Ņ€Đ°ĐŊŅĐģŅŅ†Ņ–Đš Android @@ -1020,7 +1020,7 @@ НаĐŋŅ€ŅƒĐļĐĩĐŊŅ–ŅŅ‚ŅŒ ВŅ–Đ´ŅŅ‚ĐĩĐļĐĩĐŊĐŊŅ Ņ†Đ¸ĐēĐģŅƒ ДиŅ…Đ°ĐŊĐŊŅ - PineTime (ПŅ€ĐžŅˆĐ¸Đ˛ĐēĐ° JF) + PineTime (МŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐ° JF) TLW64 ВŅ‡ĐžŅ€Đ° ĐĄŅŒĐžĐŗОдĐŊŅ– @@ -1035,21 +1035,21 @@ ЙоĐŗĐ° ĐĄĐēĐ°ĐēĐ°ĐģĐēĐ° ЕĐģŅ–ĐŋŅ‚иŅ‡ĐŊиК Ņ‚Ņ€ĐĩĐŊĐ°ĐļĐĩŅ€ - ВĐĩĐģĐžŅĐ¸ĐŋĐĩĐ´ Ņƒ ĐŋŅ€Đ¸ĐŧŅ–Ņ‰ĐĩĐŊĐŊŅ– + ВĐĩĐģĐžŅ‚Ņ€ĐĩĐŊĐ°ĐļĐĩŅ€ ПĐģаваĐŊĐŊŅ (вŅ–Đ´ĐēŅ€Đ¸Ņ‚Đ° вОда) - ВибĐĩŅ€Ņ–Ņ‚ŅŒ ŅŅ€ĐģиĐēи Đ´ĐģŅ ĐĩĐēŅ€Đ°ĐŊĐ° ĐŗОдиĐŊĐŊиĐēĐ° - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Mi Band 5. + ВибĐĩŅ€Ņ–Ņ‚ŅŒ ŅŅ€ĐģиĐēи Đ´ĐģŅ ĐĩĐēŅ€Đ°ĐŊĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚Đ° + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Mi Band 5. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit T-Rex. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit T-Rex. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ — .res, Đ° Đ´Đ°ĐģŅ– — .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ .res, Đ° в ĐēŅ–ĐŊŅ†Ņ– .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°. ВаĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēŅ– Đļ, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚Đ°ĐēŅ– ŅĐ°ĐŧŅ–, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Sony SWR12 ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ @@ -1102,7 +1102,7 @@ ĐĄĐžĐŊ МĐĩŅ‚Đ° Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– ĐĄĐĩĐēŅƒĐŊĐ´ĐžĐŧŅ–Ņ€ - ĐĸиŅ…иК Ņ€ĐĩĐļиĐŧ + НĐĩ Ņ‚ŅƒŅ€ĐąŅƒĐ˛Đ°Ņ‚и Alexa ВŅ–Đ´Đ´Đ°ĐģĐĩĐŊĐĩ ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐēĐ°ĐŧĐĩŅ€ĐžŅŽ ВиĐŧĐēĐŊŅƒŅ‚и СвŅƒĐē Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊŅƒ @@ -1142,12 +1142,12 @@ ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž йаŅ‚Đ°Ņ€ĐĩŅŽ МŅ–ĐŊŅ–ĐŧĐ°ĐģŅŒĐŊĐ° ĐēŅ–ĐģŅŒĐēŅ–ŅŅ‚ŅŒ ĐēŅ€ĐžĐēŅ–в ĐŊĐ° Ņ…виĐģиĐŊŅƒ Đ´ĐģŅ виСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– МŅ–ĐŊŅ–ĐŧĐ°ĐģŅŒĐŊĐ° ĐēŅ–ĐģŅŒĐēŅ–ŅŅ‚ŅŒ ĐēŅ€ĐžĐēŅ–в ĐŊĐ° Ņ…виĐģиĐŊŅƒ Đ´ĐģŅ виСĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ĐąŅ–ĐŗŅƒ - ДовĐļиĐŊĐ° ĐēŅ€ĐžĐēŅƒ (ŅĐŧ.) + ДовĐļиĐŊĐ° ĐēŅ€ĐžĐēŅƒ в ŅĐŧ Мова Ņ–ĐŊŅ‚ĐĩŅ€Ņ„ĐĩĐšŅŅƒ - ГодиĐŊĐŊиĐē СавŅ–ĐąŅ€ŅƒŅ”, ŅĐēŅ‰Đž Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊ вŅ–Đ´\'Ņ”Đ´ĐŊĐ°Ņ”Ņ‚ŅŒŅŅ вŅ–Đ´ ĐŗОдиĐŊĐŊиĐēĐ° + БŅ€Đ°ŅĐģĐĩŅ‚ вŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚иĐŧĐĩ, ŅĐēŅ‰Đž Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊ вŅ–Đ´\'Ņ”Đ´ĐŊĐ°Ņ”Ņ‚ŅŒŅŅ вŅ–Đ´ ĐąŅ€Đ°ŅĐģĐĩŅ‚Ņƒ ПŅ€ĐžŅ‚ивŅ‚Ņ€Đ°Ņ‚Đ° ІĐŊŅ‚ĐĩŅ€Đ˛Đ°Đģ ĐŊĐ°ĐŗĐ°Đ´ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đž ĐŗŅ–Đ´Ņ€Đ°Ņ‚Đ°Ņ†Ņ–ŅŽ (Ņ…в) - ГодиĐŊĐŊиĐē СавŅ–ĐąŅ€ŅƒŅ”, Ņ‰ĐžĐą ĐŊĐ°ĐŗĐ°Đ´Đ°Ņ‚и ĐŋŅ€Đž ĐŋиŅ‚Ņ‚Ņ + БŅ€Đ°ŅĐģĐĩŅ‚ вŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚иĐŧĐĩ, Ņ‰ĐžĐą ĐŊĐ°ĐŗĐ°Đ´Đ°Ņ‚и ваĐŧ ĐŋĐžĐŋиŅ‚и вОди НаĐŗĐ°Đ´ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đž ĐŗŅ–Đ´Ņ€Đ°Ņ‚Đ°Ņ†Ņ–ŅŽ НайĐŊиĐļŅ‡Đ¸Đš ĐŋŅƒĐģŅŒŅ: %1$d \nНайвиŅ‰Đ¸Đš ĐŋŅƒĐģŅŒŅ: %2$d @@ -1183,25 +1183,25 @@ ВвĐĩĐ´ĐĩĐŊиК ĐēĐģŅŽŅ‡ авŅ‚ĐĩĐŊŅ‚иŅ„Ņ–ĐēĐ°Ņ†Ņ–Ņ— ĐŊĐĩĐ´Ņ–ĐšŅĐŊиК! ЗаŅ‚иŅĐŊŅ–Ņ‚ŅŒ Đ´ĐģŅ Ņ€ĐĩĐ´Đ°ĐŗŅƒĐ˛Đ°ĐŊĐŊŅ. ĐŖĐŧĐžĐļĐģивĐģŅŽŅ” ĐŋĐžŅˆŅƒĐē ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ ĐŋĐž Bluetooth, ĐŊавŅ–Ņ‚ŅŒ ĐēĐžĐģи вŅ–ĐŊ ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊиК ВидиĐŧиК ĐēĐžĐģи ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊĐž - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Neo. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Neo. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ вŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit X. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ вŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit X. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! - Ви ОйŅ€Đ°Đģи вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ ĐŋŅ€ĐžŅˆĐ¸Đ˛Đēи %s ĐŊĐ° Amazfit Verge Lite. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Verge Lite. \n -\nПаĐŧ\'ŅŅ‚Đ°ĐšŅ‚Đĩ, Ņ‰Đž ŅĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ Ņ‚Ņ€Đĩйа вŅŅ‚Đ°ĐŊОвиŅ‚и .fw, ĐŋĐžŅ‚Ņ–Đŧ .res, Đ° в ĐēŅ–ĐŊŅ†Ņ– .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ Ņ‚Ņ€Đĩйа вŅŅ‚Đ°ĐŊОвиŅ‚и Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ .res, Đ° в ĐēŅ–ĐŊŅ†Ņ– .gps. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°. ВаĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и .res Ņ– .gps, ŅĐēŅ‰Đž Ņ†Ņ– Ņ„Đ°ĐšĐģи Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēŅ– Đļ, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģи .res Ņ– .gps, ŅĐēŅ‰Đž вОĐŊи Ņ‚Đ°ĐēŅ– ŅĐ°ĐŧŅ–, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊŅ–. \n -\nВИ ДІЄĐĸЕ НА ВЛАСНИЙ РИЗИК! +\nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Đ Ņ–Đē 6 ĐŧŅ–ŅŅŅ†Ņ–в 3 ĐŧŅ–ŅŅŅ†Ņ– @@ -1211,11 +1211,11 @@ ДĐĩĐŊŅŒ Đ Ņ–вĐĩĐŊŅŒ СаŅ€ŅĐ´Ņƒ ШŅƒĐēĐ°Ņ‚и %1$s\? - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Mi Band 6. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Mi Band 6. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, Đ° ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! ДŅ–Ņ— ĐŊĐ°ĐģĐ°ŅˆŅ‚ОваĐŊŅ– Ņ‚ŅƒŅ‚ ĐąŅƒĐ´Đĩ ĐŋĐžĐēаСаĐŊĐž Ņƒ СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ Commute ĐŊĐ° ĐŗОдиĐŊĐŊиĐēŅƒ. ĐŖ вŅ–ĐēŅ– ви СĐŊаКдĐĩŅ‚Đĩ вŅ–Đ´ĐžĐŧĐžŅŅ‚Ņ– ŅĐē ОйŅ€ĐžĐąĐģŅŅ‚и ĐŊĐ°ĐŧŅ–Ņ€Đ¸ ŅŅ‚вОŅ€ĐĩĐŊŅ– Ņ†Đ¸Đŧи Đ´Ņ–ŅĐŧи. @@ -1258,7 +1258,7 @@ ЗаŅŅ‚ĐžŅŅƒĐ˛Đ°Ņ‚и Ņ‡ĐžŅ€ĐŊĐĩ Ņ‚ĐģĐž Đ´ĐģŅ Ņ‚ĐĩĐŧĐŊĐžŅ— Ņ‚ĐĩĐŧи КаĐģŅ–ĐąŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ŅŅ‚Ņ€Ņ–ĐģĐžĐē ĐŗОдиĐŊĐŊиĐēĐ° ВŅ–Đ´ĐŋŅ€Đ°Đ˛ĐēĐ° Ņ‚Đ° СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅ–в - НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚и Ņ„ŅƒĐŊĐēŅ†Ņ–Ņ— Ņ„Ņ–СиŅ‡ĐŊиŅ… ĐēĐŊĐžĐŋĐžĐē ĐŗОдиĐŊĐŊиĐēĐ° + НаĐģĐ°ŅˆŅ‚ŅƒĐšŅ‚Đĩ Ņ„ŅƒĐŊĐēŅ†Ņ–Ņ— Ņ„Ņ–СиŅ‡ĐŊиŅ… ĐēĐŊĐžĐŋĐžĐē ĐŗОдиĐŊĐŊиĐēĐ° ФŅ–СиŅ‡ĐŊŅ– ĐēĐŊĐžĐŋĐēи НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ†Đ¸Ņ„ĐĩŅ€ĐąĐģĐ°Ņ‚Đ° ĐĸŅ€Đ¸Đ˛Đ°ĐģŅ–ŅŅ‚ŅŒ (Ņƒ ĐŧŅ) @@ -1303,8 +1303,8 @@ Đ ĐĩСŅƒĐģŅŒŅ‚Đ°Ņ‚и виĐŧŅ–Ņ€ŅŽĐ˛Đ°ĐŊŅŒ ВиĐŧŅ–Ņ€ŅŽĐ˛Đ°ĐŊĐŊŅ АŅ€Ņ‚ĐĩŅ€Ņ–Đ°ĐģŅŒĐŊиК Ņ‚иŅĐē - ВиĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ€ŅƒŅ… Ņ€ŅƒĐē, ĐēĐžĐģи ĐŗОдиĐŊĐŊиĐē ĐŊĐĩ ОдŅĐŗĐŊĐĩĐŊĐž - ВиĐŧĐēĐŊŅƒŅ‚и ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ°, ĐēĐžĐģи ĐŗОдиĐŊĐŊиĐē ĐŊĐĩ ОдŅĐŗĐŊĐĩĐŊĐž + ВиĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ€ŅƒŅ… Ņ€ŅƒĐē, ĐēĐžĐģи ĐąŅ€Đ°ŅĐģĐĩŅ‚ ĐŊĐĩ ОдŅĐŗĐŊĐĩĐŊĐž + ВиĐŧĐēĐŊŅƒŅ‚и ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ°, ĐēĐžĐģи ĐąŅ€Đ°ŅĐģĐĩŅ‚ ĐŊĐĩ ОдŅĐŗĐŊĐĩĐŊĐž ЗаоŅ‰Đ°Đ´ĐļĐĩĐŊĐŊŅ ĐĩĐŊĐĩŅ€ĐŗŅ–Ņ— ІĐŧОвŅ–Ņ€ĐŊŅ–ŅŅ‚ŅŒ Đ´ĐžŅ‰Ņƒ ĐĨвиĐģиĐŊи Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– @@ -1328,7 +1328,7 @@ ЧаŅ ŅĐŊŅƒ FitPro ЗвŅƒĐēОвиК ŅĐ¸ĐŗĐŊĐ°Đģ - НŅ–Ņ‡ĐžĐŗĐž ĐŋŅ€ĐžŅĐģŅƒŅ…ОвŅƒĐ˛Đ°Ņ‚и (1) + Nothing Ear (1) ВиŅĐ˛ĐģĐĩĐŊĐŊŅ ОдŅĐŗĐ°ĐŊĐŊŅ ĐŊĐ° вŅƒŅ…Đ° ВŅ–Đ´Ņ‚вОŅ€ĐĩĐŊĐŊŅ/СŅƒĐŋиĐŊĐēĐ° ĐŧŅƒĐˇĐ¸Đēи СаĐģĐĩĐļĐŊĐž вŅ–Đ´ Ņ‚ĐžĐŗĐž, Ņ‡Đ¸ ОдŅĐŗĐŊĐĩĐŊĐž ĐŊавŅƒŅˆĐŊиĐēи АŅƒĐ´Ņ–ĐžŅ€ĐĩĐļиĐŧ @@ -1340,8 +1340,8 @@ ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°Ņ‚и ŅĐŋиŅĐžĐē СаŅŅ‚ĐžŅŅƒĐŊĐēŅ–в Đ´ĐģŅâ€Ļ ЗабоŅ€ĐžĐŊиŅ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ С вийŅ€Đ°ĐŊиŅ… СаŅŅ‚ĐžŅŅƒĐŊĐēŅ–в Đ ĐĩĐļиĐŧ ĐŋŅ€Đ¸Đ˛Đ°Ņ‚ĐŊĐžŅŅ‚Ņ– ĐŋОвŅ–Đ´ĐžĐŧĐģĐĩĐŊŅŒ - ПоĐēаС вĐŧŅ–ŅŅ‚Ņƒ - ĐĄŅ…ОваŅ‚и вĐŧŅ–ŅŅ‚ + ПоĐēаСŅƒĐ˛Đ°Ņ‚и ŅƒĐ˛ĐĩŅŅŒ вĐŧŅ–ŅŅ‚ + ПŅ€Đ¸Ņ…ОвŅƒĐ˛Đ°Ņ‚и ŅƒĐ˛ĐĩŅŅŒ вĐŧŅ–ŅŅ‚ ĐĄĐŋиŅĐžĐē СаŅŅ‚ĐžŅŅƒĐŊĐēŅ–в ЗаŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐŊĐĩ ĐŋОвиĐŊĐĩĐŊ ĐąŅƒŅ‚и вийиŅ€Đ°ĐŊиĐŧ Đ´ĐģŅ ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ПоŅ‚Ņ€Ņ–ĐąĐŊĐž вийŅ€Đ°Ņ‚и СаŅŅ‚ĐžŅŅƒĐŊĐžĐē Đ´ĐģŅ ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ @@ -1359,7 +1359,7 @@ ĐĄĐĩĐŊŅĐžŅ€ĐŊĐĩ ĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ ВиĐŧĐēĐŊŅƒŅ‚и ĐŋОдŅ–Ņ— Ņ‚ĐžŅ€ĐēĐ°ĐŊĐŊŅ ЕĐēŅĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģŅŒĐŊĐĩ - ГŅƒŅ‡ĐŊŅ–ŅŅ‚ŅŒ дОвĐēŅ–ĐģĐģŅ + ГŅƒŅ‡ĐŊŅ–ŅŅ‚ŅŒ ŅĐĩŅ€ĐĩдОвиŅ‰Đ° ФОĐēŅƒŅ ĐŗĐžĐģĐžŅŅƒ ПŅ–Đ´ŅĐ¸ĐģĐĩĐŊĐŊŅ ĐŗĐžĐģĐžŅŅƒ ЛŅ–виК @@ -1379,7 +1379,7 @@ КĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊавĐēĐžĐģиŅˆĐŊŅ–Đŧ СвŅƒĐēĐžĐŧ ЗĐŊиĐļĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ вŅ–Ņ‚Ņ€Ņƒ ЗĐŊиĐļĐĩĐŊĐŊŅ Ņ‚иŅĐēŅƒ Ņ‡ĐĩŅ€ĐĩС ĐŊавĐēĐžĐģиŅˆĐŊŅ–Đš ŅˆŅƒĐŧ - ЗаĐŋОйŅ–ĐŗĐ°ĐŊĐŊŅ вŅ–Đ´Ņ‡ŅƒŅ‚Ņ‚ŅŽ Ņ‚иŅĐēŅƒ Ņƒ вŅƒŅ…Đ°Ņ… Са ĐŊĐĩвиĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ Đ°ĐēŅ‚ивĐŊĐžĐŗĐž ŅˆŅƒĐŧĐžĐŋŅ€Đ¸Đ´ŅƒŅˆĐĩĐŊĐŊŅ + ЗаĐŋОйŅ–ĐŗĐ°ĐŊĐŊŅ вŅ–Đ´Ņ‡ŅƒŅ‚Ņ‚ŅŽ Ņ‚иŅĐēŅƒ Ņƒ вŅƒŅ…Đ°Ņ…, ĐēĐžĐģи ĐŊĐĩ виĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒŅ”Ņ‚ŅŒŅŅ Đ°ĐēŅ‚ивĐŊĐĩ ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ ЗоŅĐĩŅ€ĐĩдиŅ‚иŅŅ ĐŊĐ° ĐŗĐžĐģĐžŅŅ– ВиĐŧĐē. ĐĄĐŋĐĩŅ€ĐĩĐ´Ņƒ @@ -1396,8 +1396,8 @@ ПŅ–Đ´ŅĐ¸ĐģĐĩĐŊĐŊŅ йаŅŅ–в ВĐģĐ°ŅĐŊŅ– ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ 1 400 - 2,5Đē - 6,3Đē + 2,5k + 6,3k ĐĄĐĩĐŊŅĐžŅ€ĐŊĐĩ ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Đš ĐŗĐžĐģĐžŅĐžĐ˛Ņ– вĐēаСŅ–вĐēи АвŅ‚ОвиĐŧĐēĐŊĐĩĐŊĐŊŅ ĐļивĐģĐĩĐŊĐŊŅ @@ -1408,9 +1408,9 @@ ЗвиŅ‡Đ°ĐšĐŊŅ– Galaxy Buds Live Sony WH-1000XM3 - АĐēŅ‚ивĐŊĐĩ ŅˆŅƒĐŧОСаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ + АĐēŅ‚ивĐŊĐĩ ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ Đ ĐĩĐļиĐŧ - ШŅƒĐŧОСаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ + ШŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ НавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē Đ Ņ–вĐĩĐŊŅŒ ĐŊавĐēĐžĐģиŅˆĐŊŅ–Ņ… СвŅƒĐēŅ–в ĐĄĐŋĐĩŅ€ĐĩĐ´Ņƒ ĐŋŅ€Đ°Đ˛ĐžŅ€ŅƒŅ‡ @@ -1426,14 +1426,14 @@ ВŅ€ŅƒŅ‡ĐŊŅƒ ВĐģĐ°ŅĐŊиК 1 ВĐģĐ°ŅĐŊиК 2 - 1Đē + 1k 3 ĐŗОдиĐŊи РОСŅĐģĐ°ĐąĐģĐĩĐŊиК ВоĐēĐ°Đģ МовĐģĐĩĐŊĐŊŅ 1 ĐŗОдиĐŊĐ° ЧиŅŅ‚иК йаŅ - 16Đē + 16k ПоĐēаСаŅ‚и вŅ–Đ´ĐžĐŧĐžŅŅ‚Ņ– ĐŋŅ€Đž Đ´Ņ–ŅĐģŅŒĐŊŅ–ŅŅ‚ŅŒ ĐŊĐ° ĐēĐ°Ņ€Ņ‚Ņ†Ņ– ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ ĐĄĐžĐŊ ПоĐēаСаŅ‚и СаĐŗĐ°ĐģŅŒĐŊŅƒ ĐēŅ–ĐģŅŒĐēŅ–ŅŅ‚ŅŒ ĐēŅ€ĐžĐēŅ–в @@ -1560,13 +1560,13 @@ НĐĩвŅ–Đ´ĐžĐŧĐž РОСĐŋĐžŅ‡Đ°Ņ‚и ОĐŋŅ‚иĐŧŅ–СŅƒĐ˛Đ°Ņ‚и - ОĐŋŅ‚иĐŧŅ–СаŅ‚ĐžŅ€ ĐŋŅ€Đ¸ĐŗĐŊŅ–Ņ‡ĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ - НаŅ‚иŅĐŊŅ–Ņ‚ŅŒ, Ņ‰ĐžĐą СаĐŋŅƒŅŅ‚иŅ‚и ĐžĐŋŅ‚иĐŧŅ–СаŅ‚ĐžŅ€ ĐŋŅ€Đ¸ĐŗĐŊŅ–Ņ‡ĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ. + ОĐŋŅ‚иĐŧŅ–СаŅ‚ĐžŅ€ ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ + НаŅ‚иŅĐŊŅ–Ņ‚ŅŒ, Ņ‰ĐžĐą СаĐŋŅƒŅŅ‚иŅ‚и ĐžĐŋŅ‚иĐŧŅ–СаŅ‚ĐžŅ€ ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ. АŅ‚ĐŧĐžŅŅ„ĐĩŅ€ĐŊиК Ņ‚иŅĐē ЗаĐŋŅƒŅĐēâ€Ļ НĐĩ СаĐŋŅƒŅ‰ĐĩĐŊĐž АĐŊĐ°ĐģŅ–СŅƒĐ˛Đ°ĐŊĐŊŅâ€Ļ - ОĐŋŅ‚иĐŧŅ–СаŅ‚ĐžŅ€ ĐŋŅ€Đ¸ĐŗĐŊŅ–Ņ‡ĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ + ОĐŋŅ‚иĐŧŅ–СаŅ‚ĐžŅ€ ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ ВиĐŧŅ–Ņ€ŅŽĐ˛Đ°ĐŊĐŊŅ ŅŅ‚Đ°ĐŊŅƒ ŅĐĩŅ€ĐĩдОвиŅ‰Đ°â€Ļ ВиĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒĐšŅ‚Đĩ ĐŊавŅƒŅˆĐŊиĐēи, ŅĐē СаСвиŅ‡Đ°Đš. Đ¯ĐēŅ‰Đž ŅĐĩŅ€ĐĩдОвиŅ‰Đĩ айО Đ°Ņ‚ĐŧĐžŅŅ„ĐĩŅ€ĐŊиК Ņ‚иŅĐē СĐŧŅ–ĐŊŅŽŅŽŅ‚ŅŒŅŅ, СаĐŋŅƒŅŅ‚Ņ–Ņ‚ŅŒ ĐžĐŋŅ‚иĐŧŅ–СаŅ‚ĐžŅ€ Ņ‰Đĩ Ņ€Đ°Đˇ. ВиĐŧŅ–Ņ€ŅŽĐ˛Đ°ĐŊĐŊŅ Đ°Ņ‚ĐŧĐžŅŅ„ĐĩŅ€ĐŊĐžĐŗĐž Ņ‚иŅĐēŅƒâ€Ļ @@ -1620,18 +1620,18 @@ ЗвиŅ‡Đ°ĐšĐŊĐž ЧŅƒŅ‚ĐģивО ХвŅ–Ņ‚ОвиК Ņ‡Đ°Ņ - НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚и ĐŗОдиĐŊĐŊиĐē Đ´ĐģŅ Ņ–ĐŊŅˆĐ¸Ņ… Ņ‡Đ°ŅĐžĐ˛Đ¸Ņ… ĐŋĐžŅŅŅ–в + НаĐģĐ°ŅˆŅ‚ŅƒĐšŅ‚Đĩ ĐŗОдиĐŊĐŊиĐēи Đ´ĐģŅ Ņ–ĐŊŅˆĐ¸Ņ… Ņ‡Đ°ŅĐžĐ˛Đ¸Ņ… ĐŋĐžŅŅŅ–в ПодŅ€ĐžĐąĐ¸Ņ†Ņ– ŅĐ˛Ņ–Ņ‚ОвОĐŗĐž ĐŗОдиĐŊĐŊиĐēĐ° НĐĩĐŧĐ°Ņ” вŅ–ĐģŅŒĐŊиŅ… ĐēĐžĐŧŅ–Ņ€ĐžĐē ЧаŅĐžĐ˛Đ¸Đš ĐŋĐžŅŅ МŅ–Ņ‚ĐēĐ° ĐĸиĐŋи Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊŅŒ БŅ–Đŗ ĐŊадвОŅ€Ņ– - ВибĐĩŅ€Ņ–Ņ‚ŅŒ ŅĐēŅ– Ņ‚иĐŋи Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– ĐŋĐžĐēаСŅƒĐ˛Đ°Ņ‚и ĐŊĐ° ĐĩĐēŅ€Đ°ĐŊŅ– Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊŅŒ + ВибĐĩŅ€Ņ–Ņ‚ŅŒ Ņ‚иĐŋи Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– Đ´ĐģŅ ĐŋĐžĐēаСŅƒ ĐŊĐ° ĐĩĐēŅ€Đ°ĐŊŅ– Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊŅŒ ФŅ€Đ¸ŅŅ‚Đ°ĐšĐģ ВĐĩĐģĐžŅ—Сда ĐŊадвОŅ€Ņ– - ЕĐģŅ–ĐŋŅ‚иŅ‡ĐŊиК - ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŋŅ€ĐžŅŅ‚Ņ–Đš + ЕĐģŅ–ĐŋŅ‚иŅ‡ĐŊиК Ņ‚Ņ€ĐĩĐŊĐ°ĐļĐĩŅ€ + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŊĐĩĐ°ĐēŅ‚ивĐŊŅ–ŅŅ‚ŅŒ НаĐŗĐ°Đ´ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đž ĐŋОдŅ–Ņ— ЗĐŊĐ°ĐšŅ‚и ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš ШайĐģĐžĐŊи вŅ–ĐąŅ€Đ°Ņ†Ņ–Đš @@ -1640,24 +1640,24 @@ Galaxy Buds Pro ВŅ–Đ´Ņ‚вОŅ€ĐĩĐŊĐŊŅ виĐēĐģиĐēŅ–в Ņ‡ĐĩŅ€ĐĩС ĐŊавŅƒŅˆĐŊиĐēи, ĐēĐžĐģи вОĐŊи Ņƒ ваŅˆĐ¸Ņ… вŅƒŅ…Đ°Ņ… АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐĩ ĐŋĐĩŅ€ĐĩĐŧиĐēĐ°Ņ” ĐŊавŅƒŅˆĐŊиĐēи ĐŧŅ–Đļ ĐŋОв\'ŅĐˇĐ°ĐŊиĐŧи ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅĐŧи - ГŅƒŅ‡ĐŊŅ–ŅŅ‚ŅŒ ĐŊавĐēĐžĐģиŅˆĐŊŅŒĐžĐŗĐž ŅĐĩŅ€ĐĩдОвиŅ‰Đ° СĐģŅ–ва + ГŅƒŅ‡ĐŊŅ–ŅŅ‚ŅŒ ŅĐĩŅ€ĐĩдОвиŅ‰Đ° СĐģŅ–ва НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊавĐēĐžĐģиŅˆĐŊŅŒĐžĐŗĐž СвŅƒĐēŅƒ НавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē ĐŋŅ–Đ´ Ņ‡Đ°Ņ виĐēĐģиĐēŅƒ - АĐēŅ‚ивĐŊиК Ņ€Ņ–вĐĩĐŊŅŒ ĐŋŅ€Đ¸ĐŗĐŊŅ–Ņ‡ĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ + Đ Ņ–вĐĩĐŊŅŒ Đ°ĐēŅ‚ивĐŊĐžĐŗĐž ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ ВиŅĐžĐēиК ПĐĩŅ€ĐĩĐŧиĐēĐ°ĐŊĐŊŅ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ° ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐģŅ–вОŅ€ŅƒŅ‡ ПĐĩŅ€ĐĩĐŧиĐēĐ°ĐŊĐŊŅ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ° ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đ°Đ˛ĐžŅ€ŅƒŅ‡ - АĐēŅ‚ивĐŊĐĩ ĐŋŅ€Đ¸ĐŗĐŊŅ–Ņ‡ĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ + АĐēŅ‚ивĐŊĐĩ ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ ГŅƒŅ‡ĐŊŅ–ŅŅ‚ŅŒ ВŅ–Đ´ Đŧ\'ŅĐēĐžĐŗĐž Đ´Đž Ņ‡Đ¸ŅŅ‚ĐžĐŗĐž - ПŅ€Đ¸ĐŗĐŊŅ–Ņ‡ĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ ←→ ВиĐŧĐēĐŊĐĩĐŊĐž - НавĐēĐžĐģиŅˆĐŊŅ” ŅĐĩŅ€ĐĩдОвиŅ‰Đĩ ←→ ВиĐŧĐēĐŊĐĩĐŊĐž + ШŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ ←→ ВиĐŧĐēĐŊĐĩĐŊĐž + ĐĄĐĩŅ€ĐĩдОвиŅ‰Đĩ ←→ ВиĐŧĐēĐŊĐĩĐŊĐž КоĐŊŅ‚Ņ€ĐžĐģŅŒ ŅˆŅƒĐŧŅƒ ВиŅĐ˛ĐģĐĩĐŊĐŊŅ ĐŗĐžĐģĐžŅŅƒ ПодвŅ–ĐšĐŊĐĩ Ņ‚ĐžŅ€ĐēĐ°ĐŊĐŊŅ ĐēŅ€Đ°ŅŽ ЗавĐĩŅ€ŅˆĐĩĐŊĐŊŅ ĐŋŅ–ŅĐģŅ Ņ‚иŅˆŅ– Са: 15 ŅĐĩĐēŅƒĐŊĐ´ - ГŅƒŅ‡ĐŊŅ–ŅŅ‚ŅŒ ĐŊавĐēĐžĐģиŅˆĐŊŅŒĐžĐŗĐž ŅĐĩŅ€ĐĩдОвиŅ‰Đ° вĐŋŅ€Đ°Đ˛Đž + ГŅƒŅ‡ĐŊŅ–ŅŅ‚ŅŒ ŅĐĩŅ€ĐĩдОвиŅ‰Đ° ŅĐŋŅ€Đ°Đ˛Đ° ЧŅ–Ņ‚ĐēĐž Ņ‡ŅƒŅ‚и вĐģĐ°ŅĐŊиК ĐŗĐžĐģĐžŅ ĐŋŅ–Đ´ Ņ‡Đ°Ņ виĐēĐģиĐēŅ–в ПĐģавĐŊĐĩ ĐŋĐĩŅ€ĐĩĐŧиĐēĐ°ĐŊĐŊŅ С\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ ПаŅ€Đ°ĐŧĐĩŅ‚Ņ€Đ¸ ĐŊавĐēĐžĐģиŅˆĐŊŅŒĐžĐŗĐž СвŅƒĐēŅƒ @@ -1671,7 +1671,7 @@ ДозвоĐģиŅ‚и ĐēĐžĐŊŅ‚Ņ€ĐžĐģŅŒ ŅˆŅƒĐŧŅƒ ĐŋŅ–Đ´ Ņ‡Đ°Ņ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ ĐģиŅˆĐĩ ОдĐŊĐžĐŗĐž ĐŊавŅƒŅˆĐŊиĐēĐ° БаĐģĐ°ĐŊŅ ĐĸĐžĐŊ ĐŊавĐēĐžĐģиŅˆĐŊŅŒĐžĐŗĐž СвŅƒĐēŅƒ - ПŅ€Đ¸ĐŗĐŊŅ–Ņ‡ĐĩĐŊĐŊŅ ŅˆŅƒĐŧŅƒ ←→ НавĐēĐžĐģиŅˆĐŊŅ” ŅĐĩŅ€ĐĩдОвиŅ‰Đĩ + ШŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ ←→ ĐĄĐĩŅ€ĐĩдОвиŅ‰Đĩ ĐŖвŅ–ĐŧĐēĐŊŅƒŅ‚и ĐŊавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē Ņ– авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐĩ ŅŅ‚иŅˆĐĩĐŊĐŊŅ вŅ–Đ´Ņ‚вОŅ€ĐĩĐŊĐŊŅ ĐŋŅ–ŅĐģŅ виŅĐ˛ĐģĐĩĐŊĐŊŅ ĐŗĐžĐģĐžŅŅƒ ВиŅĐ˛ĐģĐĩĐŊĐŊŅ ĐŋОдвŅ–ĐšĐŊĐžĐŗĐž Ņ‚ĐžŅ€ĐēĐ°ĐŊĐŊŅ, ĐŊавŅ–Ņ‚ŅŒ ŅĐēŅ‰Đž ĐŊĐĩ Ņ‚ĐžŅ€ĐēĐ°Ņ‚иŅŅ Ņ‚Đ°Ņ‡ĐŋĐ°Đ´Đ° 10 ŅĐĩĐēŅƒĐŊĐ´ @@ -1689,12 +1689,12 @@ 145 ŅƒĐ´/Ņ…в 125 ŅƒĐ´/Ņ…в 135 ŅƒĐ´/Ņ…в - ĐĄĐŋĐžŅŅ‚ĐĩŅ€ĐĩĐļĐĩĐŊĐŊŅ Са Ņ€Ņ–вĐŊĐĩĐŧ ĐŊĐ°ĐŋŅ€ŅƒĐļĐĩĐŊĐžŅŅ‚Ņ– ĐŋŅ–Đ´ Ņ‡Đ°Ņ вŅ–Đ´ĐŋĐžŅ‡Đ¸ĐŊĐēŅƒ - ВŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ ĐŗОдиĐŊĐŊиĐēĐ°, ĐēĐžĐģи ĐŋŅƒĐģŅŒŅ ĐŋĐĩŅ€ĐĩвиŅ‰ŅƒŅ” ОйĐŧĐĩĐļĐĩĐŊĐŊŅ, ĐąĐĩС ĐąŅƒĐ´ŅŒ-ŅĐēĐžŅ— ĐžŅ‡ĐĩвидĐŊĐžŅ— Ņ„Ņ–СиŅ‡ĐŊĐžŅ— Đ´Ņ–ŅĐģŅŒĐŊĐžŅŅ‚Ņ– ĐŋŅ€ĐžŅ‚ŅĐŗĐžĐŧ ĐžŅŅ‚Đ°ĐŊĐŊŅ–Ņ… 10 Ņ…виĐģиĐŊ. ĐĻŅ Ņ„ŅƒĐŊĐēŅ†Ņ–Ņ ĐĩĐēŅĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģŅŒĐŊĐ°, Ņ– ĐŊĐĩ ĐąŅƒĐģĐ° Đ´ĐžŅŅ‚Đ°Ņ‚ĐŊŅŒĐž виĐŋŅ€ĐžĐąŅƒĐ˛Đ°ĐŊĐ°. + ĐĄĐŋĐžŅŅ‚ĐĩŅ€Ņ–ĐŗĐ°Ņ‚и Ņ€Ņ–вĐĩĐŊŅŒ ĐŊĐ°ĐŋŅ€ŅƒĐļĐĩĐŊĐžŅŅ‚Ņ– ĐŋŅ–Đ´ Ņ‡Đ°Ņ вŅ–Đ´ĐŋĐžŅ‡Đ¸ĐŊĐēŅƒ + БŅ€Đ°ŅĐģĐĩŅ‚ вŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚иĐŧĐĩ, ĐēĐžĐģи ĐŋŅƒĐģŅŒŅ ĐŋĐĩŅ€ĐĩвиŅ‰Đ¸Ņ‚ŅŒ ОйĐŧĐĩĐļĐĩĐŊĐŊŅ, ĐąĐĩС ŅĐ˛ĐŊĐžŅ— Ņ„Ņ–СиŅ‡ĐŊĐžŅ— Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– ĐŋŅ€ĐžŅ‚ŅĐŗĐžĐŧ ĐžŅŅ‚Đ°ĐŊĐŊŅ–Ņ… 10 Ņ…виĐģиĐŊ. ĐĻŅ Ņ„ŅƒĐŊĐēŅ†Ņ–Ņ ĐĩĐēŅĐŋĐĩŅ€Đ¸ĐŧĐĩĐŊŅ‚Đ°ĐģŅŒĐŊĐ° Đš ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ‚ĐŊŅŒĐž виĐŋŅ€ĐžĐąŅƒĐ˛Đ°ĐŊĐ°. ĐĄĐŋĐžŅŅ‚ĐĩŅ€ĐĩĐļĐĩĐŊĐŊŅ Са ĐŊĐ°ĐŋŅ€ŅƒĐļĐĩĐŊŅ–ŅŅ‚ŅŽ ĐĄĐŋĐžŅŅ‚ĐĩŅ€ĐĩĐļĐĩĐŊĐŊŅ Са Đ´Ņ–ŅĐģŅŒĐŊŅ–ŅŅ‚ŅŽ АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐž СйŅ–ĐģŅŒŅˆŅƒĐ˛Đ°Ņ‚и Ņ‡Đ°ŅŅ‚ĐžŅ‚Ņƒ ĐŋĐĩŅ€ĐĩвŅ–Ņ€Đēи ĐŋŅƒĐģŅŒŅŅƒ, ĐēĐžĐģи ĐŗОдиĐŊĐŊиĐē виŅĐ˛ĐģŅŅ” Ņ„Ņ–СиŅ‡ĐŊŅ– вĐŋŅ€Đ°Đ˛Đ¸, Ņ‰ĐžĐą ĐŋŅ–двиŅ‰Đ¸Ņ‚и Ņ‚ĐžŅ‡ĐŊŅ–ŅŅ‚ŅŒ ĐžŅ…ĐžĐŋĐģĐĩĐŊĐŊŅ ĐŋŅƒĐģŅŒŅŅƒ. - НаĐģĐ°ŅˆŅ‚ŅƒĐšŅ‚Đĩ ĐŧĐĩĐļŅ– ŅĐŋĐžŅŅ‚ĐĩŅ€ĐĩĐļĐĩĐŊĐŊŅ Са ĐŋŅƒĐģŅŒŅĐžĐŧ Ņ– ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊŅŒ + НаĐģĐ°ŅˆŅ‚ŅƒĐšŅ‚Đĩ ŅĐŋĐžŅŅ‚ĐĩŅ€ĐĩĐļĐĩĐŊĐŊŅ Ņ‚Đ° ĐŧĐĩĐļŅ– ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊŅŒ ĐŋŅ€Đž ĐŋŅƒĐģŅŒŅ ĐĄĐŋĐžŅŅ‚ĐĩŅ€ĐĩĐļĐĩĐŊĐŊŅ Са ĐŋŅƒĐģŅŒŅĐžĐŧ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚и ŅĐŋĐžŅŅ‚ĐĩŅ€ĐĩĐļĐĩĐŊĐŊŅ Са ĐŋŅƒĐģŅŒŅĐžĐŧ ДозвоĐģиŅ‚и Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Ņ–ĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚Ņƒ @@ -1704,9 +1704,9 @@ ĐĸĐĩĐēŅŅ‚ Ņƒ виĐŗĐģŅĐ´Ņ– Ņ€Đ°ŅŅ‚Ņ€ĐžĐ˛Đ¸Ņ… СОйŅ€Đ°ĐļĐĩĐŊŅŒ Đ¯ĐēŅ‰Đž ŅĐģОвО ĐŊĐĩ ĐŧĐžĐļĐĩ ĐąŅƒŅ‚и вŅ–Đ´Ņ‚вОŅ€ĐĩĐŊĐž ŅˆŅ€Đ¸Ņ„Ņ‚ĐžĐŧ ĐŗОдиĐŊĐŊиĐēĐ°, ĐŋĐĩŅ€ĐĩŅ‚вОŅ€ŅŽĐ˛Đ°Ņ‚и КОĐŗĐž ĐŊĐ° Ņ€Đ°ŅŅ‚Ņ€ĐžĐ˛Đĩ СОйŅ€Đ°ĐļĐĩĐŊĐŊŅ в Gadgetbridge Ņ– ĐŋĐžĐēаСŅƒĐ˛Đ°Ņ‚и Ņ€Đ°ŅŅ‚Ņ€ĐžĐ˛Đĩ СОйŅ€Đ°ĐļĐĩĐŊĐŊŅ ĐŊĐ° ĐŗОдиĐŊĐŊиĐēŅƒ ВŅ–Đ´ŅŅ‚ĐĩĐļĐĩĐŊĐŊŅ Ņ„Ņ–Ņ‚ĐŊĐĩŅ-СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ - ЗаĐŋŅƒŅĐēĐ°Ņ‚и/ĐŋŅ€Đ¸ĐŋиĐŊŅŅ‚и вŅ–Đ´ŅŅ‚ĐĩĐļĐĩĐŊĐŊŅ Ņ„Ņ–Ņ‚ĐŊĐĩŅ-СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ ĐŊĐ° Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊŅ–, ĐēĐžĐģи GPS-Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊĐŊŅ Ņ€ĐžĐˇĐŋĐžŅ‡Đ°Ņ‚Đž ĐŊĐ° ĐŗОдиĐŊĐŊиĐēŅƒ + ЗаĐŋŅƒŅĐēĐ°Ņ‚и/ĐŋŅ€Đ¸ĐŋиĐŊŅŅ‚и вŅ–Đ´ŅŅ‚ĐĩĐļĐĩĐŊĐŊŅ Ņ„Ņ–Ņ‚ĐŊĐĩŅ-СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ ĐŊĐ° Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊŅ–, ĐēĐžĐģи GPS-Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊĐŊŅ Ņ€ĐžĐˇĐŋĐžŅ‡Đ°Ņ‚Đž ĐŊĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚Ņ– НадŅĐ¸ĐģĐ°Ņ‚и GPS ĐŋŅ–Đ´ Ņ‡Đ°Ņ Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊĐŊŅ - НадŅĐ¸ĐģĐ°Ņ‚и ĐŋĐžŅ‚ĐžŅ‡ĐŊĐĩ ĐŧŅ–ŅŅ†ĐĩĐŋĐĩŅ€ĐĩĐąŅƒĐ˛Đ°ĐŊĐŊŅ GPS ĐŊĐ° ĐŗОдиĐŊĐŊиĐē ĐŋŅ–Đ´ Ņ‡Đ°Ņ Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊĐŊŅ + НадŅĐ¸ĐģĐ°Ņ‚и ĐŋĐžŅ‚ĐžŅ‡ĐŊĐĩ ĐŧŅ–ŅŅ†ĐĩĐŋĐĩŅ€ĐĩĐąŅƒĐ˛Đ°ĐŊĐŊŅ GPS ĐŊĐ° ĐąŅ€Đ°ŅĐģĐĩŅ‚ ĐŋŅ–Đ´ Ņ‡Đ°Ņ Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊĐŊŅ GPS-ŅŅ‚ĐĩĐļĐĩĐŊĐŊŅ Gadgetbridge GPS НадŅĐ¸ĐģĐ°ĐŊĐŊŅ ĐŧŅ–ŅŅ†ĐĩĐŋĐĩŅ€ĐĩĐąŅƒĐ˛Đ°ĐŊĐŊŅ GPS ĐŊĐ° ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš %1$d @@ -1866,9 +1866,9 @@ КиŅĐĩĐŊŅŒ Ņƒ ĐēŅ€ĐžĐ˛Ņ– Đ¯ŅĐēŅ€Đ°Đ˛Ņ–ŅŅ‚ŅŒ ĐĩĐēŅ€Đ°ĐŊĐ° ПоĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊĐŊŅ ĐŋŅ€Đž ĐŋŅƒĐģŅŒŅ - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Xiaomi Smart Band 7. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Xiaomi Smart Band 7. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! З\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ @@ -1877,11 +1877,11 @@ ЧаŅ ĐĸŅ€ĐĩĐŊŅƒĐ˛Đ°ĐŊĐŊŅ ДиŅĐŋĐģĐĩĐš - ДаĐŊŅ– ĐŋŅ€Đž ĐŋĐžĐŗОдŅƒ ĐēĐĩŅˆŅƒĐ˛Đ°Ņ‚иĐŧŅƒŅ‚ŅŒŅŅ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŋĐĩŅ€ĐĩСаĐŋŅƒŅĐēŅ–в СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ. + ПоĐŗОдĐŊŅ– Đ´Đ°ĐŊŅ– ĐēĐĩŅˆŅƒĐ˛Đ°Ņ‚иĐŧŅƒŅ‚ŅŒŅŅ Са ĐēĐžĐļĐŊĐžĐŗĐž ĐŋĐĩŅ€ĐĩСаĐŋŅƒŅĐēŅƒ СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ. ЗавĐļди - КĐĩŅˆŅƒĐ˛Đ°Ņ‚и Đ´Đ°ĐŊŅ– ĐŋŅ€Đž ĐŋĐžĐŗОдŅƒ + КĐĩŅˆŅƒĐ˛Đ°Ņ‚и ĐŋĐžĐŗОдĐŊŅ– Đ´Đ°ĐŊŅ– 45 ŅƒĐ´/Ņ…в - ВŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ ĐŗОдиĐŊĐŊиĐēĐ° Đ´ĐģŅ ĐŋОвŅ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ ваŅ, ŅĐēŅ‰Đž СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ĐŊĐ°ĐŋŅ€ŅƒĐļĐĩĐŊĐžŅŅ‚Ņ– ĐŋĐžĐŊĐ°Đ´ 80 + БŅ€Đ°ŅĐģĐĩŅ‚ вŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚иĐŧĐĩ, Ņ‰ĐžĐą ĐŋОвŅ–Đ´ĐžĐŧиŅ‚и ваŅ, ŅĐēŅ‰Đž СĐŊĐ°Ņ‡ĐĩĐŊĐŊŅ ĐŊĐ°ĐŋŅ€ŅƒĐļĐĩĐŊĐžŅŅ‚Ņ– ĐŋĐžĐŊĐ°Đ´ 80 ПоŅ€Ņ–Đŗ ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ SPO2 14 ŅĐĩĐēŅƒĐŊĐ´ 15 ŅĐĩĐēŅƒĐŊĐ´ @@ -1902,9 +1902,9 @@ Bluetooth Intent API Flipper zero ДозвоĐģиŅ‚и ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ С\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅĐŧ Bluetooth Ņ‡ĐĩŅ€ĐĩС Intent API - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTS 3. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTS 3. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Amazfit GTS 3 @@ -1916,9 +1916,9 @@ ПŅ–Đ´ Ņ‡Đ°Ņ ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ Đ´Đž ĐŗОдиĐŊĐŊиĐēĐ° ĐŋĐĩŅ€ĐĩСаĐŋиŅŅƒĐ˛Đ°Ņ‚и вŅŅ– ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊĐ° ĐŊŅŒĐžĐŧŅƒ. ВŅ–Đ´Ņ…иĐģиŅ‚и Amazfit GTR 3 - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTR 3. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTR 3. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! ДозвоĐģиŅ‚и ŅŅ‚ĐžŅ€ĐžĐŊĐŊŅ–Đŧ СаŅŅ‚ĐžŅŅƒĐŊĐēĐ°Đŧ СĐŧŅ–ĐŊŅŽĐ˛Đ°Ņ‚и ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ @@ -1995,9 +1995,9 @@ АвŅ‚ĐžŅŅĐēŅ€Đ°Đ˛Ņ–ŅŅ‚ŅŒ ЕĐēŅ€Đ°ĐŊ СавĐļди ŅĐ˛Ņ–Ņ‚иŅ‚ŅŒŅŅ ЗаŅ‚иŅĐēĐ°ĐŊĐŊŅ вĐĩŅ€Ņ…ĐŊŅŒĐžŅ— ĐēĐŊĐžĐŋĐēи - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTR 4. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTR 4. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! ПоŅˆŅƒĐē ŅŅƒĐŋŅƒŅ‚ĐŊиĐēŅ–в @@ -2030,7 +2030,7 @@ НаĐŗĐ°Đ´ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đž СаĐēŅ–ĐŊŅ‡ĐĩĐŊĐŊŅ Ņ‚ĐĩŅ€ĐŧŅ–ĐŊŅƒ Đ´Ņ–Ņ— AGPS КаŅ‚ĐĩĐŗĐžŅ€Ņ–Ņ— Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊŅŒ Đ´ĐģŅ авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐžĐŗĐž виŅĐ˛ĐģĐĩĐŊĐŊŅ ОĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ - ПоĐēаС ĐĩĐēŅ€Đ°ĐŊĐ° ŅĐŊŅƒ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŋŅ€ĐžĐąŅƒĐ´ĐļĐĩĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ° С Ņ€ĐĩĐļиĐŧŅƒ ŅĐŊŅƒ, Ņ‰ĐžĐą СĐŧĐĩĐŊŅˆĐ¸Ņ‚и вŅ–двОĐģŅ–ĐēĐ°ĐŊĐŊŅ + ПоĐēаС ĐĩĐēŅ€Đ°ĐŊĐ° ŅĐŊŅƒ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŋŅ€ĐžĐąŅƒĐ´ĐļĐĩĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ° С Ņ€ĐĩĐļиĐŧŅƒ ŅĐŊŅƒ, Ņ‰ĐžĐą СĐŊиСиŅ‚и вŅ–двĐĩŅ€Ņ‚Đ°ĐŊĐŊŅ ŅƒĐ˛Đ°Đŗи КĐŊĐžĐŋĐēи СĐģŅ–ва КĐŊĐžĐŋĐēи ŅĐŋŅ€Đ°Đ˛Đ° НаĐŋŅ€ŅĐŧĐžĐē ĐŊĐžŅŅ–ĐŊĐŊŅ @@ -2063,9 +2063,9 @@ ВиĐģŅƒŅ‡Đ¸Ņ‚и ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ\? ĐĻĐĩ ĐŋŅ€Đ¸ĐˇĐ˛ĐĩĐ´Đĩ Đ´Đž ŅĐēидаĐŊĐŊŅ ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊŅŒ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ Đ´ĐģŅ вŅŅ–Ņ… ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊиŅ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—в. Ви вĐŋĐĩвĐŊĐĩĐŊŅ–\? Amazfit Band 7 - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Band 7. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Band 7. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! ĐŖвŅ–ĐŧĐēĐŊĐĩĐŊĐž @@ -2075,14 +2075,14 @@ ĐĄĐĩĐŊŅĐžŅ€ĐŊĐĩ ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ Galaxy Buds2 Amazfit GTS 4 - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTS 4. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTS 4. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTS 4 Mini. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTS 4 Mini. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Amazfit GTS 4 Mini @@ -2094,13 +2094,13 @@ ДозвоĐģиŅ‚и Ņ–ĐŊŅ–Ņ†Ņ–Đ°Ņ†Ņ–ŅŽ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–СаŅ†Ņ–Ņ— Đ´Ņ–Đš ДозвоĐģиŅ‚и Ņ–ĐŊŅ–Ņ†Ņ–ŅŽĐ˛Đ°ĐŊĐŊŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–СаŅ†Ņ–Ņ— Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– Ņ‡ĐĩŅ€ĐĩС Intent API ДозвоĐģиŅ‚и Ņ–ĐŊŅ–Ņ†Ņ–ŅŽĐ˛Đ°Ņ‚и ĐĩĐēŅĐŋĐžŅ€Ņ‚ йаСи Đ´Đ°ĐŊиŅ… Ņ‡ĐĩŅ€ĐĩС Intent API - ШŅƒĐŧОСаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ, НавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē, ВиĐŧĐē - ШŅƒĐŧОСаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ, ĐŊавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē + ШŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ, НавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē, ВиĐŧĐē + ШŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ, ĐŊавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē НавĐēĐžĐģиŅˆĐŊŅ–Đš СвŅƒĐē, ВиĐŧĐē ШвидĐēиК Đ´ĐžŅŅ‚ŅƒĐŋ (ĐŋОдвŅ–ĐšĐŊĐĩ Ņ‚ĐžŅ€ĐēĐ°ĐŊĐŊŅ) ШвидĐēиК Đ´ĐžŅŅ‚ŅƒĐŋ (ĐŋĐžŅ‚Ņ€Ņ–ĐšĐŊĐĩ Ņ‚ĐžŅ€ĐēĐ°ĐŊĐŊŅ) Đ ĐĩĐļиĐŧи ĐēĐŊĐžĐŋĐēи ĐēĐĩŅ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊавĐēĐžĐģиŅˆĐŊŅ–Đŧ СвŅƒĐēĐžĐŧ - ШŅƒĐŧОСаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ, ВиĐŧĐē + ШŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ, ВиĐŧĐē WeChat Pay ЧаŅ ĐžĐŊОвĐģĐĩĐŊĐŊŅ AGPS ĐĸĐĩŅ€ĐŧŅ–ĐŊ Đ´Ņ–Ņ— AGPS @@ -2112,9 +2112,9 @@ ПоĐŧиĐģĐēĐ° ĐŋŅ–Đ´ Ņ‡Đ°Ņ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ AsteroidOS Amazfit T-Rex 2 - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit T-Rex 2. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit T-Rex 2. \n -\nВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! ВĐĩŅĐģŅƒĐ˛Đ°ĐŊĐŊŅ @@ -2151,7 +2151,7 @@ ЗŅƒĐŋиĐŊиŅ‚и FTP-ŅĐĩŅ€Đ˛ĐĩŅ€ С ĐŗОдиĐŊĐŊиĐēĐ° ĐĄŅ‚Đ°ĐŊ FTP-ŅĐĩŅ€Đ˛ĐĩŅ€Đ° ĐĨĐžŅ€Đ˛Đ°Ņ‚ŅŅŒĐēĐ° - АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐž виĐŧиĐēĐ°Ņ‚и ŅˆŅƒĐŧОСаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ, ĐēĐžĐģи ви ĐŋĐžŅ‡Đ¸ĐŊĐ°Ņ”Ņ‚Đĩ ĐŗОвОŅ€Đ¸Ņ‚и. + АвŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐž виĐŧиĐēĐ°Ņ‚и ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ, ĐēĐžĐģи ви ĐŋĐžŅ‡Đ¸ĐŊĐ°Ņ”Ņ‚Đĩ ĐŗОвОŅ€Đ¸Ņ‚и. Đ Đ°ĐŊĐēОвŅ– ĐžĐŊОвĐģĐĩĐŊĐŊŅ ПоĐēаСŅƒĐ˛Đ°Ņ‚и ĐžĐŊОвĐģĐĩĐŊĐŊŅ Ņ‰ĐžŅ€Đ°ĐŊĐēŅƒ КаŅ‚ĐĩĐŗĐžŅ€Ņ–Ņ— Ņ€Đ°ĐŊĐēОвиŅ… ĐžĐŊОвĐģĐĩĐŊŅŒ @@ -2180,7 +2180,7 @@ ПŅ€ĐžĐ´ĐžĐ˛ĐļиŅ‚и ĐŊĐ°Ņ‚иŅĐēĐ°ĐŊĐŊŅ ШвидĐēĐ° ŅƒĐ˛Đ°ĐŗĐ° ЗаŅ‚иŅĐēĐ°ĐŊĐŊŅ - ШŅƒĐŧОСаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ ←→ НавĐēĐžĐģиŅˆĐŊŅ” ŅĐĩŅ€ĐĩдОвиŅ‰Đĩ ←→ ВиĐŧĐēĐŊĐĩĐŊĐž + ШŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ ←→ ĐĄĐĩŅ€ĐĩдОвиŅ‰Đĩ ←→ ВиĐŧĐēĐŊĐĩĐŊĐž Đ ĐĩĐļиĐŧи ĐēĐŊĐžĐŋĐžĐē - ДовŅ–Đ´ĐēĐ° MI AI КаŅ€Ņ‚Đēи @@ -2203,9 +2203,9 @@ ĐĄĐŧĐ°Ņ€Đ°ĐŗдОвĐĩ ĐŧŅ–ŅŅŅ‡ĐŊĐĩ ŅŅĐšĐ˛Đž ІĐŊĐ´ĐĩĐēŅ ŅĐēĐžŅŅ‚Ņ– ĐŋОвŅ–Ņ‚Ņ€Ņ Amazfit GTR 3 Pro - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit GTR 3 Pro. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit GTR 3 Pro. \n -\nВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Sony WH-1000XM5 @@ -2226,7 +2226,7 @@ ПоĐŧиĐģĐēĐ° СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŧĐ°Ņ€ŅˆŅ€ŅƒŅ‚Ņƒ Gpx ЗаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ĐŧĐ°Ņ€ŅˆŅ€ŅƒŅ‚Ņƒ Gpx СавĐĩŅ€ŅˆĐĩĐŊĐž ВиваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ gpx ĐŧĐ°Ņ€ŅˆŅ€ŅƒŅ‚Ņƒ - НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚и ĐēĐžĐŊŅ‚Đ°ĐēŅ‚и ĐŊĐ° ĐŗОдиĐŊĐŊиĐēŅƒ + НаĐģĐ°ŅˆŅ‚ŅƒĐšŅ‚Đĩ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚и ĐŊĐ° ĐŗОдиĐŊĐŊиĐēŅƒ ІĐŧ\'Ņ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚Ņƒ ĐŋĐžŅ€ĐžĐļĐŊŅ” НоĐŧĐĩŅ€ Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊŅƒ НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚и ĐēĐžĐŊŅ‚Đ°ĐēŅ‚и @@ -2235,7 +2235,7 @@ ĐŖ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ĐŊĐĩĐŧĐ°Ņ” вŅ–ĐģŅŒĐŊиŅ… ŅĐģĐžŅ‚Ņ–в Đ´ĐģŅ ĐēĐžĐŊŅ‚Đ°ĐēŅ‚Ņ–в (ŅƒŅŅŒĐžĐŗĐž ŅĐģĐžŅ‚Ņ–в: %1$s) ІĐŧ\'Ņ КоĐŊŅ‚Đ°ĐēŅ‚ĐŊŅ– Đ´Đ°ĐŊŅ– - Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģиŅ‚и \'%1$s\'\? + Ви Đ´Ņ–ĐšŅĐŊĐž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģиŅ‚и \'%1$s\'? КоĐŊŅ‚Đ°ĐēŅ‚ĐŊиК ĐŊĐžĐŧĐĩŅ€ ĐŋĐžŅ€ĐžĐļĐŊŅ–Đš НавŅ–ĐŗĐ°Ņ†Ņ–Ņ НадŅĐ¸ĐģĐ°Ņ‚и ĐŊавŅ–ĐŗĐ°Ņ†Ņ–ŅŽ ĐŊĐ° ĐŗОдиĐŊĐŊиĐē @@ -2273,14 +2273,14 @@ ПŅ–ĐēŅ‚ĐžĐŗŅ€Đ°ĐŧĐ° ŅŅ‚Đ°ĐŊŅƒ ЖŅƒŅ€ĐŊĐ°Đģ СĐŧŅ–ĐŊ БŅ–ĐģŅŒŅˆĐĩâ€Ļ - ГАРАЗД + ГаŅ€Đ°ĐˇĐ´ ĐŠĐž ĐŊОвОĐŗĐž PAI ĐŊĐ° Ņ‚иĐļĐ´ĐĩĐŊŅŒ PAI ĐŊĐ° ĐŧŅ–ŅŅŅ†ŅŒ +%d %d Ņ…в - ДĐĩĐŊĐŊиК ĐŋŅ€Đ¸Ņ€Ņ–ŅŅ‚ PAI - ВŅŅŒĐžĐŗĐž PAI + ДĐĩĐŊĐŊиК ĐŋŅ€Đ¸Ņ€Ņ–ŅŅ‚ + ĐŖŅŅŒĐžĐŗĐž Назва ĐŋĐ°ĐēŅƒĐŊĐēĐ° Catima ĐŖŅŅ‚Đ°ĐŊОвиŅ‚и Catima ХиĐŊŅ…Ņ€ĐžĐŊŅ–СŅƒĐ˛Đ°Ņ‚и ĐģиŅˆĐĩ ĐŋĐĩвĐŊŅ– ĐŗŅ€ŅƒĐŋи @@ -2355,29 +2355,29 @@ Ņ€ŅƒŅ‡ĐēĐ° ĐŋĐĩŅ€ĐĩŅ‚ŅĐŗŅƒĐ˛Đ°ĐŊĐŊŅ ІĐŊŅ–Ņ†Ņ–ŅŽĐ˛Đ°Ņ‚и ĐŋОвĐŊŅƒ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–СаŅ†Ņ–ŅŽ вŅŅ–Ņ… Đ´Đ°ĐŊиŅ… Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– ПоĐēаСŅƒĐ˛Đ°Ņ‚и ĐļŅƒŅ€ĐŊĐ°Đģ СĐŧŅ–ĐŊ ĐŋŅ–Đ´ Ņ‡Đ°Ņ СаĐŋŅƒŅĐēŅƒ - ПоĐēаС ĐļŅƒŅ€ĐŊĐ°ĐģŅƒ СĐŧŅ–ĐŊ ĐŊĐ°ĐšĐŊОвŅ–ŅˆĐžŅ— вĐĩŅ€ŅŅ–Ņ— ĐŋŅ–ŅĐģŅ ĐžĐŊОвĐģĐĩĐŊĐŊŅ Gadgetbridge + ПоĐēаСŅƒĐ˛Đ°Ņ‚и ĐļŅƒŅ€ĐŊĐ°Đģ СĐŧŅ–ĐŊ ĐŊĐ°ĐšĐŊОвŅ–ŅˆĐžŅ— вĐĩŅ€ŅŅ–Ņ— ĐŋŅ–ŅĐģŅ ĐžĐŊОвĐģĐĩĐŊĐŊŅ Gadgetbridge ПовĐŊĐ° ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–СаŅ†Ņ–Ņ ĐĻĐĩ Ņ–ĐŊŅ–Ņ†Ņ–ŅŽŅ” ĐŋОвĐŊŅƒ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–СаŅ†Ņ–ŅŽ вŅŅ–Ņ… Đ´Đ°ĐŊиŅ… Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ. ВиĐēĐžĐŊĐ°ĐŊĐŊŅ Ņ‚Ņ€Đ¸Đ˛Đ°Ņ‚иĐŧĐĩ ĐēŅ–ĐģŅŒĐēĐ° Ņ…виĐģиĐŊ. ДиĐŊĐ°ĐŧŅ–Ņ‡ĐŊŅ– ĐēĐžĐģŅŒĐžŅ€Đ¸ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊŅ– ĐŊĐ° ваŅˆĐžĐŧŅƒ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—, ĐģиŅˆĐĩ Android 12+ ĐŋŅ–Đ´Ņ‚Ņ€Đ¸ĐŧŅƒŅ” Ņ†ŅŽ Ņ„ŅƒĐŊĐēŅ†Ņ–ŅŽ. Gadgetbridge виĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒĐ˛Đ°Ņ‚иĐŧĐĩ ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊŅ– ĐēĐžĐģŅŒĐžŅ€Đ¸ Material 3. Zepp Coach ПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: Đ´ĐģŅ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ Ņ‚ĐĩĐŧи диĐŊĐ°ĐŧŅ–Ņ‡ĐŊиŅ… ĐēĐžĐģŅŒĐžŅ€Ņ–в ваĐŧ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž ŅƒĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚и \"КоĐģŅŒĐžŅ€Đ¸ ŅˆĐŋĐ°ĐģĐĩŅ€\" айО \"ПаĐģŅ–Ņ‚Ņ€Đ° ĐēĐžĐģŅŒĐžŅ€Ņ–в\" Ņƒ ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ… СОвĐŊŅ–ŅˆĐŊŅŒĐžĐŗĐž виĐŗĐģŅĐ´Ņƒ Android 12+. Đ¯ĐēŅ‰Đž ви Ņ†ŅŒĐžĐŗĐž ĐŊĐĩ СŅ€ĐžĐąĐ¸Ņ‚Đĩ, Gadgetbridge виĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒĐ˛Đ°Ņ‚иĐŧĐĩ ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊŅ– ĐēĐžĐģŅŒĐžŅ€Đ¸ Material 3. Amazfit Bip 3 Pro - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Bip 3 Pro. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Bip 3 Pro. \n -\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ — .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. +\nĐĄĐŋĐžŅ‡Đ°Ņ‚ĐēŅƒ вŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ .fw, ĐŋĐžŅ‚Ņ–Đŧ .res. ВаŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .fw. \n -\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°. ВаĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚ĐžŅ‡ĐŊĐž Ņ‚Đ°ĐēиК, ŅĐē Ņ– Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. +\nПŅ€Đ¸ĐŧŅ–Ņ‚ĐēĐ°: ваĐŧ ĐŊĐĩ ĐŋĐžŅ‚Ņ€Ņ–ĐąĐŊĐž вŅŅ‚Đ°ĐŊОвĐģŅŽĐ˛Đ°Ņ‚и Ņ„Đ°ĐšĐģ .res, ŅĐēŅ‰Đž вŅ–ĐŊ Ņ‚Đ°ĐēиК ŅĐ°ĐŧиК, ŅĐē Ņ€Đ°ĐŊŅ–ŅˆĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊиК. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Amazfit Cheetah Pro - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° Amazfit Cheetah Pro. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ Amazfit Cheetah Pro. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! - Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° %s. + Ви СйиŅ€Đ°Ņ”Ņ‚ĐĩŅŅŒ ŅƒŅŅ‚Đ°ĐŊОвиŅ‚и ĐŧŅ–ĐēŅ€ĐžĐŋŅ€ĐžĐŗŅ€Đ°ĐŧŅƒ %s ĐŊĐ° ваŅˆ %s. \n -\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ .zip-Ņ„Đ°ĐšĐģŅƒ ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. +\nПŅ–ŅĐģŅ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐŊŅ Ņ„Đ°ĐšĐģŅƒ .zip ваŅˆ ĐŗОдиĐŊĐŊиĐē ĐŋĐĩŅ€ĐĩСаваĐŊŅ‚Đ°ĐļиŅ‚ŅŒŅŅ. \n \nПРОДОВЖĐŖЙĐĸЕ НА ВЛАСНИЙ РИЗИК! Amazfit Cheetah (Square) @@ -2407,4 +2407,254 @@ ІŅĐŋĐ°ĐŊŅŅŒĐēĐ° (ХША) На ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ‚ĐŊŅŒĐž вŅ–ĐģŅŒĐŊĐžĐŗĐž ĐŋŅ€ĐžŅŅ‚ĐžŅ€Ņƒ НадŅ–ŅĐģĐ°Ņ‚и Ņ‚ĐĩĐēŅŅ‚ ĐŊĐ° ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš + ПоŅ‚ĐžŅ‡ĐŊиК MTU %1$d СаĐŊиСŅŒĐēиК. ĐŖвŅ–ĐŧĐēĐŊŅ–Ņ‚ŅŒ виŅĐžĐēиК MTU Ņƒ ĐŊĐ°ĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅŅ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ Ņ‚Đ° вŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐšŅ‚Đĩ/ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐšŅ‚Đĩ ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš. + Mi Band HRX + ĐŖвŅ–ĐŧĐēĐŊŅƒŅ‚и виŅĐ˛ĐģĐĩĐŊĐŊŅ ĐŊОвОŅ— Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ–, Ņ‰Đž ĐŧĐ°Ņ” ŅƒŅŅƒĐŊŅƒŅ‚и ĐŋŅ€ĐžĐąĐģĐĩĐŧи С виŅĐ˛ĐģĐĩĐŊĐŊŅĐŧ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—в. ВиĐŧĐēĐŊŅ–Ņ‚ŅŒ Ņ†ĐĩĐš ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€, ŅĐēŅ‰Đž Ņƒ ваŅ виĐŊиĐēĐģи ĐŋŅ€ĐžĐąĐģĐĩĐŧи ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐŋĐžŅˆŅƒĐēŅƒ айО ŅĐŋĐžĐģŅƒŅ‡ĐĩĐŊĐŊŅ С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ”Đŧ. + ĐŖвŅ–ĐŧĐēĐŊŅƒŅ‚и виŅĐ˛ĐģĐĩĐŊĐŊŅ ĐŊОвОŅ— Đ°ĐēŅ‚ивĐŊĐžŅŅ‚Ņ– + ЛŅ–вОŅ€ŅƒŅ‡ + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ Đ´Ņ–ŅĐģŅŒĐŊĐžŅŅ‚Ņ– + БŅ–ĐģиК + ХиĐŊŅ…Ņ€ĐžĐŊŅ–СаŅ†Ņ–Ņ Đ´Đ°ĐŊиŅ… ĐŋŅ€Đž Ņ„ĐžĐŊОвŅƒ Đ´Ņ–ŅĐģŅŒĐŊŅ–ŅŅ‚ŅŒ + ПŅ€Đ°Đ˛ĐžŅ€ŅƒŅ‡ + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊĐ° + ЗĐĩĐģĐĩĐŊиК + ЖовŅ‚иК + Sony Wena 3 + ЧĐĩŅ€Đ˛ĐžĐŊиК + ЗĐĩĐģĐĩĐŊĐž-ĐąĐģĐ°ĐēиŅ‚ĐŊиК + По ŅĐĩŅ€ĐĩдиĐŊŅ– + ФŅ–ĐžĐģĐĩŅ‚ОвиК + ХиĐŊŅ–Đš + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ вŅ…Ņ–Đ´ĐŊиŅ… виĐēĐģиĐēŅ–в + ЗаĐŋŅƒŅĐē Đ°ĐēŅ‚ивĐŊĐžĐŗĐž Ņ‚Đ°ĐšĐŧĐĩŅ€Đ° + ПŅ–ĐēŅ‚ĐžĐŗŅ€Đ°ĐŧĐ° НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊŅŒ СавĐļди ĐŋĐžĐēаСŅƒŅ”Ņ‚ŅŒŅŅ в ĐēŅ–ĐŊŅ†Ņ– + 3 Ņ€Đ°ĐˇĐ¸ + ЗабĐģĐžĐēŅƒĐ˛Đ°Ņ‚и Qrio + ЧаŅ виĐŧĐēĐŊĐĩĐŊĐŊŅ + КоŅ€ĐžŅ‚ĐēĐĩ + КаĐģĐžŅ€Ņ–Ņ— + БĐĩС ŅĐ˛Ņ–Ņ‚ĐģОдŅ–Ода + ПоŅĐģĐ°ĐąĐģŅŽĐ˛Đ°ĐŊĐ° + ІĐŊŅ‚ĐĩĐģĐĩĐēŅ‚ŅƒĐ°ĐģŅŒĐŊĐ° вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ + Riiiver + ДозвоĐģиŅ‚и Wena ĐŋĐĩŅ€Ņ–ОдиŅ‡ĐŊĐž СаĐŋиŅ‚ŅƒĐ˛Đ°Ņ‚и Gadgetbridge ĐŋŅ€Đž СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ С ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ Đ´Đ°ĐŊиŅ… ĐŋŅ€Đž Đ´Ņ–ŅĐģŅŒĐŊŅ–ŅŅ‚ŅŒ + БаĐģĐ°ĐŊŅ Suica + ĐĄĐĩŅ€ĐĩĐ´ĐŊŅ + БĐĩСĐŋĐĩŅ€ĐĩŅ€Đ˛ĐŊĐ° + ЛŅ–Ņ‡Đ¸ĐģŅŒĐŊиĐē ĐēŅ€ĐžĐēŅ–в + Qrio + ПоŅ‚ĐžŅ‡ĐŊиК Ņ‡Đ°Ņ + ĐĄĐģĐ°ĐąĐēĐ° + ПŅ–ĐēŅ‚ĐžĐŗŅ€Đ°Đŧи Đ´ĐžĐŧĐ°ŅˆĐŊŅŒĐžĐŗĐž ĐĩĐēŅ€Đ°ĐŊĐ° + ПоĐŗОда Ņƒ ĐŋĐ°ĐŊĐĩĐģŅ– ŅŅ‚Đ°ĐŊŅƒ + За ĐĻĐĩĐģŅŒŅŅ–Ņ”Đŧ + ПоŅĐ¸ĐģŅŽĐ˛Đ°ĐŊĐ° + Базова + БŅ–ĐģŅŒŅˆĐ¸Đš Ņ€ĐžĐˇĐŧŅ–Ņ€ ŅˆŅ€Đ¸Ņ„Ņ‚Ņƒ + ЕĐŊĐĩŅ€ĐŗŅ–Ņ ĐžŅ€ĐŗĐ°ĐŊŅ–СĐŧŅƒ + ДвŅ–Ņ‡Ņ– + ПŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐž виĐŧиĐēĐ°Ņ‚иĐŧĐĩŅ‚ŅŒŅŅ Ņ‚Đ° вĐŧиĐēĐ°Ņ‚иĐŧĐĩŅ‚ŅŒŅŅ Са вĐēаСаĐŊиĐŧ Ņ€ĐžĐˇĐēĐģĐ°Đ´ĐžĐŧ + ВĐŧиĐēĐ°Ņ‚и ĐĩĐēŅ€Đ°ĐŊ, ĐēĐžĐģи ви дивиŅ‚ĐĩŅŅ ĐŊĐ° ŅĐ˛ĐžŅ” СаĐŋ\'ŅŅŅ‚Ņ + ЗбŅ–ĐģŅŒŅˆĐ¸Ņ‚и Ņ€ĐžĐˇĐŧŅ–Ņ€ ŅˆŅ€Đ¸Ņ„Ņ‚Ņƒ в ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ–, ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅŅ… Ņ‚ĐžŅ‰Đž. + ПодвŅ–ĐšĐŊĐĩ ĐŊĐ°Ņ‚иŅĐēĐ°ĐŊĐŊŅ + ШвидĐēĐ° + РОСйĐģĐžĐēŅƒĐ˛Đ°Ņ‚и Qrio + ШĐēĐ°ĐģĐ° Ņ‚ĐĩĐŧĐŋĐĩŅ€Đ°Ņ‚ŅƒŅ€Đ¸ + ОдиĐŊ Ņ€Đ°Đˇ + ОŅ‚Ņ€Đ¸ĐŧŅƒĐ˛Đ°Ņ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž виĐēĐģиĐēи + ПоĐēаСŅƒĐ˛Đ°Ņ‚и СĐŊĐ°Ņ‡ĐžĐē ĐŋĐžŅ‚ĐžŅ‡ĐŊиŅ… ŅƒĐŧОв Ņƒ вĐĩŅ€Ņ…ĐŊŅŒĐžĐŧŅƒ ĐģŅ–вОĐŧŅƒ ĐēŅƒŅ‚ĐēŅƒ ĐŗĐžĐģОвĐŊĐžĐŗĐž ĐĩĐēŅ€Đ°ĐŊĐ° + Wena Pay + КĐŊĐžĐŋĐēĐ° Đ´Ņ–Ņ— + ЧаŅ вŅ–Đ´ĐŊОвĐģĐĩĐŊĐŊŅ + ВŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ вŅ…Ņ–Đ´ĐŊĐžĐŗĐž виĐēĐģиĐēŅƒ + КоĐģŅ–Ņ€ Ņ–ĐŊдиĐēĐ°Ņ‚ĐžŅ€Đ° вŅ…Ņ–Đ´ĐŊĐžĐŗĐž виĐēĐģиĐēŅƒ + ХиŅ€ĐĩĐŊĐ° + ВибĐĩŅ€Ņ–Ņ‚ŅŒ, Ņ‡Đ¸ виĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒŅ” ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš ŅˆĐēĐ°ĐģŅƒ Са ĐĻĐĩĐģŅŒŅŅ–Ņ”Đŧ, Ņ‡Đ¸ Са ФаŅ€ĐĩĐŊĐŗĐĩĐšŅ‚ĐžĐŧ. + ПŅ–ĐēŅ‚ĐžĐŗŅ€Đ°Đŧи ĐŧĐĩĐŊŅŽ + ПовŅ‚ĐžŅ€ĐĩĐŊĐŊŅ вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ— вŅ…Ņ–Đ´ĐŊĐžĐŗĐž виĐēĐģиĐēŅƒ + ВŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ ŅĐŋОвŅ–Ņ‰ĐĩĐŊŅŒ + БĐĩСĐŋĐĩŅ€ĐĩŅ€Đ˛ĐŊĐž + 4 Ņ€Đ°ĐˇĐ¸ + За ФаŅ€ĐĩĐŊĐŗĐĩĐšŅ‚ĐžĐŧ + ХиĐģĐ° вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ— + Đ¯ĐēŅ‰Đž виĐŧĐēĐŊĐĩĐŊĐž, ви ĐŊĐĩ ĐžŅ‚Ņ€Đ¸ĐŧŅƒĐ˛Đ°Ņ‚иĐŧĐĩŅ‚Đĩ ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž вŅ…Ņ–Đ´ĐŊŅ– виĐēĐģиĐēи ĐŊĐ° Wena + ЗаŅ‚иŅĐēĐ°ĐŊĐŊŅ + ПĐģĐ°Ņ‚Ņ–Đļ + ĐĄŅ‚ĐžŅ€Ņ–ĐŊĐēĐ° ŅŅ‚Đ°ĐŊŅƒ СаĐŧОвĐģĐĩĐŊĐŊŅ + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐēĐ° + ПовŅ‚ĐžŅ€ĐĩĐŊĐŊŅ вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ— ŅĐŋОвŅ–Ņ‰ĐĩĐŊŅŒ + ДодаŅ” СаОĐēŅ€ŅƒĐŗĐģĐĩĐŊŅ– ĐŋŅ€ŅĐŧĐžĐēŅƒŅ‚ĐŊиĐēи ĐŊавĐēĐžĐģĐž ĐŋŅ–ĐēŅ‚ĐžĐŗŅ€Đ°Đŧ Đ´ĐžĐŧĐ°ŅˆĐŊŅŒĐžĐŗĐž ĐĩĐēŅ€Đ°ĐŊĐ° + КоĐģŅ–Ņ€ ŅĐ˛Ņ–Ņ‚ĐģОдŅ–Ода ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ + АвŅ‚ОвиĐŧĐēĐŊĐĩĐŊĐŊŅ + БаĐģĐ°ĐŊŅ Edy + ПоĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊĐŊŅ + ІĐŊŅ‚ĐĩĐģĐĩĐēŅ‚ŅƒĐ°ĐģŅŒĐŊиК Đ´Ņ–Đ°ĐŋаСОĐŊ ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐēĐ° + ВиĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒĐ˛Đ°Ņ‚и виŅ€Đ°ĐˇĐŊиК диСаКĐŊ + ДĐĩĐŊŅŒ ĐŋĐžŅ‡Đ¸ĐŊĐ°Ņ”Ņ‚ŅŒŅŅ Đž + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ŅĐŋОвŅ–Ņ‰ĐĩĐŊŅŒ Đ´ĐģŅ ĐēĐžĐļĐŊĐžĐŗĐž СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ + БĐĩС вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ— + ХиĐģŅŒĐŊĐ° + ПоŅ‚Ņ€Ņ–ĐšĐŊĐ° + ЕĐēŅ€Đ°ĐŊ Đ´Ņ–ŅĐģŅŒĐŊĐžŅŅ‚Ņ– + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ŅĐŋОвŅ–Ņ‰ĐĩĐŊŅŒ + Sony WF-1000XM5 + ПŅ€Đ¸Ņ…ОвŅƒĐ˛Đ°Ņ‚и ĐģиŅˆĐĩ Ņ‚Ņ–ĐģĐž + ЛаŅ‚иŅŅŒĐēĐ° + ЧаŅŅ‚Ņ– ŅĐ¸ĐŧвОĐģи + ІĐŗĐŊĐžŅ€ŅƒĐ˛Đ°Ņ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Ņ€ĐžĐąĐžŅ‡ĐžĐŗĐž ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ + НĐĩ ĐŊĐ°Đ´ŅĐ¸ĐģĐ°Ņ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Ņ–С СаŅŅ‚ĐžŅŅƒĐŊĐēŅ–в С Ņ€ĐžĐąĐžŅ‡ĐžĐŗĐž ĐŋŅ€ĐžŅ„Ņ–ĐģŅŽ ĐŊĐ° ĐŗОдиĐŊĐŊиĐē + ЧиŅŅ‚иК ĐąŅ–ĐģиК + ІĐŗĐŊĐžŅ€ŅƒĐ˛Đ°Ņ‚и (Ņ‚иŅˆĐ°) + ĐĄĐĩŅ€ĐĩĐ´ĐŊŅ Ņ‡Đ°ŅŅ‚ĐžŅ‚Đ° ĐŧĐ°Ņ…Ņ–в + Đ Ņ–ŅˆŅƒŅ‡Ņ–ŅŅ‚ŅŒ + МŅ–ŅŅ‚Đž ŅˆĐ˛Đ¸Đ´ĐēĐžŅŅ‚Ņ– + НавŅƒŅˆĐŊиĐēи + ЗвиŅ‡Đ°ĐšĐŊиК (60 -90Ņ) + ВŅ–Đ´Ņ…иĐģиŅ‚и + ВŅ–ĐģŅŒĐŊĐĩ ĐŋĐžŅ”Đ´ĐŊĐ°ĐŊĐŊŅ + ĐĄĐēĐģĐ°Đ´ Ņ‚Ņ–ĐģĐ° + Zepp Pay + ĐĄĐŋĐžŅŅ–Đą вŅ–Đ´Ņ…иĐģĐĩĐŊĐŊŅ виĐēĐģиĐēŅƒ + ОŅ‚Ņ€Đ¸ĐŧĐ°ĐŊĐŊŅ ŅŅ‚Đ°Ņ‚иŅŅ‚иĐēи + ШвидĐēиК (30Ņ) + ЗоŅ€ŅĐŊĐĩ ĐŊĐĩйО + Amazfit Balance + НавŅ–ĐŗĐ°Ņ†Ņ–ŅŽ СаĐŋŅƒŅ‰ĐĩĐŊĐž, Đ°ĐģĐĩ СаŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐŊавŅ–ĐŗĐ°Ņ†Ņ–Ņ— ĐŊĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž ĐŊĐ° ĐŗОдиĐŊĐŊиĐēŅƒ. ĐŖŅŅ‚Đ°ĐŊОвŅ–Ņ‚ŅŒ КОĐŗĐž С МĐĩĐŊĐĩĐ´ĐļĐĩŅ€Đ° СаŅŅ‚ĐžŅŅƒĐŊĐēŅ–в. + ŅŅ€Đ´Đ¸ + ĐŧĐ°Ņ…/Ņ…в + Đ ĐĩĐļиĐŧ виĐŧŅ–Ņ€ŅŽĐ˛Đ°ĐŊĐŊŅ + Đ¯ĐēĐ° Đ´Ņ–Ņ виĐēĐžĐŊŅƒŅ”Ņ‚ŅŒŅŅ, ĐēĐžĐģи вŅ…Ņ–Đ´ĐŊиК виĐēĐģиĐē вŅ–Đ´Ņ…иĐģŅŅ”Ņ‚ŅŒŅŅ С ĐŗОдиĐŊĐŊиĐēĐ° + Amazfit Active Edge + ОŅ‚Ņ€Đ¸ĐŧĐ°ĐŊĐŊŅ Đ´Đ°ĐŊиŅ… ĐŋŅ€Đž Ņ‚ĐĩĐŧĐŋĐĩŅ€Đ°Ņ‚ŅƒŅ€Ņƒ + ĐĸĐžŅ‡ĐŊиК (3 Ņ…в) + БĐĩСĐēŅ€Đ°Ņ” ĐŊĐĩйО + Đ¯Ņ€ĐģиĐēи Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊŅŒ + МаĐēŅĐ¸ĐŧĐ°ĐģŅŒĐŊĐ° Ņ‡Đ°ŅŅ‚ĐžŅ‚Đ° ĐŧĐ°Ņ…Ņ–в + ĐĄĐŋĐ°ĐģĐ°Ņ… ĐąĐģиŅĐēавĐēи + ДовĐļиĐŊĐ° Đ´ĐžŅ€Ņ–ĐļĐēи + ĐĄĐĩŅ€ĐĩĐ´ĐŊŅ–Đš Ņ€Ņ–вĐĩĐŊŅŒ ĐēиŅĐŊŅŽ в ĐēŅ€ĐžĐ˛Ņ– + ĐŖŅŅŒĐžĐŗĐž ĐŧĐ°Ņ…Ņ–в + Đ¯Ņ€ĐģиĐēи СаŅŅ‚ĐžŅŅƒĐŊĐēŅ–в + ПоводиŅ€ + ГоŅ‚ОвĐŊŅ–ŅŅ‚ŅŒ + Amazfit Active + ĐĸĐĩŅ€ĐŧĐžĐŧĐĩŅ‚Ņ€ + Femometer Vinca II + На ĐŗОдиĐŊĐŊиĐēŅƒ ĐŊĐĩ вŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐž ĐŊавŅ–ĐŗĐ°Ņ†Ņ–ĐšĐŊиК СаŅŅ‚ĐžŅŅƒĐŊĐžĐē + ІĐŊŅˆŅ– ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž вŅ…Ņ–Đ´ĐŊŅ– виĐēĐģиĐēи (вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ/ĐŋиŅ‰Đ°ĐŊĐŊŅ) + НĐĩ Ņ‚ŅƒŅ€ĐąŅƒĐ˛Đ°Ņ‚и - ĐŖвŅ–ĐŧĐēĐŊĐĩĐŊĐž + БĐĩССвŅƒŅ‡ĐŊиК Ņ€ĐĩĐļиĐŧ Ņ‚ĐĩĐģĐĩŅ„ĐžĐŊĐ° + ĐĸиĐŋ ĐŊĐžŅŅ–ĐŊĐŊŅ + Pebble (ĐŋŅ€ŅĐļĐēĐ° ĐŊĐ° вСŅƒŅ‚Ņ‚Ņ–) + Xiaomi Smart Band 8 + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž вŅ…Ņ–Đ´ĐŊŅ– виĐēĐģиĐēи + ЗвиŅ‡Đ°ĐšĐŊиК / ВŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ + ДодаŅ‚ĐēОва ĐŧĐĩŅ‚Đ° + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Đĩ-ĐŋĐžŅˆŅ‚и + ПоŅ…Ņ–Đ´ Ņƒ ĐŗĐžŅ€Đ¸ + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Đĩ-ĐŋĐžŅˆŅ‚и (вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ/ĐŋиŅ‰Đ°ĐŊĐŊŅ) + НĐĩ Ņ‚ŅƒŅ€ĐąŅƒĐ˛Đ°Ņ‚и - ЛиŅˆĐĩ ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐē + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž СМС (вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ/ĐŋиŅ‰Đ°ĐŊĐŊŅ) + ColaCao 2023 + ПоĐēаСаŅ‚и ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Đš ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´ ĐŋОвŅ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ в СаĐŗĐžĐģОвĐēŅƒ + Redmi Watch 3 Active + ВŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ / БĐĩССвŅƒŅ‡ĐŊиК + Redmi Smart Band 2 + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐēĐ°ĐģĐĩĐŊĐ´Đ°Ņ€Ņ (вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ/ĐŋиŅ‰Đ°ĐŊĐŊŅ) + НадŅ–ŅĐģĐ°Ņ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ ĐŊĐ° ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš + НĐĩ Ņ‚ŅƒŅ€ĐąŅƒĐ˛Đ°Ņ‚и - ЛиŅˆĐĩ ваĐļĐģивŅ– + ДаĐŊŅŅŒĐēĐ° + Xiaomi Smart Band 7 Pro + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ Ņ–ĐŊŅˆĐ¸Ņ… ĐēĐ°Ņ‚ĐĩĐŗĐžŅ€Ņ–Đš (вŅ–ĐąŅ€Đ°Ņ†Ņ–Ņ/ĐŋиŅ‰Đ°ĐŊĐŊŅ) + ЧаŅ ŅŅ‚ĐžŅŅ‡Đ¸ + ЧаŅ Đ´Ņ–ŅĐģŅŒĐŊĐžŅŅ‚Ņ– + Назва СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ в ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ– + НаŅ€ŅƒŅ‡ĐŊиК ĐąŅ€Đ°ŅĐģĐĩŅ‚ + ColaCao 2021 + КоĐģŅŒŅ” (ŅˆĐ¸ĐšĐŊиК Ņ€ĐĩĐŧŅ–ĐŊĐĩŅ†ŅŒ) + Xiaomi Watch Lite + ПоĐēаСаŅ‚и ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐŊŅ–Đš ĐŋĐĩŅ€ĐĩĐŗĐģŅĐ´ ĐŋОвŅ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ в СаĐŗĐžĐģОвĐēŅƒ ĐŋОвŅ–Đ´ĐžĐŧĐģĐĩĐŊĐŊŅ, ŅĐē Ņ†Đĩ дОСвОĐģĐĩĐŊĐž ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ”Đŧ + НадŅ–ŅĐģĐ°Ņ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ + OsmAnd(+) + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŋŅ€Đž СМС + НĐĩ Ņ‚ŅƒŅ€ĐąŅƒĐ˛Đ°Ņ‚и - ВиĐŧĐēĐŊĐĩĐŊĐž + ЗвиŅ‡Đ°ĐšĐŊиК / БĐĩССвŅƒŅ‡ĐŊиК + ĐĸŅ€ĐĩĐšĐģŅ€Đ°ĐŊŅ–ĐŊĐŗ + Mi Watch Color Sport + НавŅ–ĐŗĐ°Ņ†Ņ–ĐšĐŊŅ– СаŅŅ‚ĐžŅŅƒĐŊĐēи + БŅ–Đŗ + ГŅ€Đ°Ņ„Ņ–Đē Ņ€ĐĩĐļиĐŧŅƒ ŅĐŊŅƒ + ПоĐŋĐģĐĩŅĐēĐ°ĐšŅ‚Đĩ в Đ´ĐžĐģĐžĐŊŅ–, Ņ‰ĐžĐą ŅƒĐ˛Ņ–ĐŧĐēĐŊŅƒŅ‚и ĐĩĐēŅ€Đ°ĐŊ" + ПоŅ‡Đ°Ņ‚ĐžĐē ŅĐŊŅƒ + ОŅ‚Ņ€Đ¸ĐŧŅƒĐšŅ‚Đĩ ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ, ĐēĐžĐģи ваŅˆ ĐŋĐžĐēаСĐŊиĐē ĐļиŅ‚Ņ‚Ņ”вОŅ— ĐĩĐŊĐĩŅ€ĐŗŅ–Ņ— Đ´ĐžŅŅĐŗĐŊĐĩ 30, 60 айО 100 Са ĐžŅŅ‚Đ°ĐŊĐŊŅ– 7 Đ´ĐŊŅ–в + Pixoo + ОŅ†Ņ–ĐŊĐēĐ° ĐļиŅ‚Ņ‚Ņ”вОŅ— ĐĩĐŊĐĩŅ€ĐŗŅ–Ņ— + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊавŅ–ĐŗĐ°Ņ†Ņ–Ņ— + ЗоŅĐĩŅ€ĐĩĐ´ĐļĐĩĐŊĐŊŅ + КаŅ€Ņ‚и Google + ПовŅ‚ĐžŅ€ĐŊĐĩ ĐŋĐģĐĩŅĐēĐ°ĐŊĐŊŅ виĐŧĐēĐŊĐĩ ĐĩĐēŅ€Đ°ĐŊ" + Mijia Temperature Ņ– Humidity Sensor 2 + ПŅ€ĐĩŅ„Ņ–ĐēŅ ĐŊаСви ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ С ĐŊаСвОŅŽ СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ-Đ´ĐļĐĩŅ€ĐĩĐģĐ° + ĐĄĐĩŅ€Ņ–ĐšĐŊиК ĐŊĐžĐŧĐĩŅ€ + ВиĐēĐžŅ€Đ¸ŅŅ‚ОвŅƒŅ”Ņ‚ŅŒŅŅ Đ´ĐģŅ вийОŅ€Ņƒ вĐĩŅ€ŅŅ–Ņ— OsmAnd Đ´ĐģŅ ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ + ПŅ€ĐžĐąŅƒĐ´ĐļĐĩĐŊĐŊŅ + ĐĄŅ‚Đ°Ņ‚иŅŅ‚иĐēĐ° + НĐĩ ĐŊĐ°ĐģĐ°ŅˆŅ‚ОваĐŊĐž + Назва ĐŋĐ°ĐēŅƒĐŊĐēĐ° OsmAnd + ЕĐēŅ€Đ°ĐŊ виĐŧĐēĐŊĐĩŅ‚ŅŒŅŅ ĐŋŅ–ŅĐģŅ Ņ‚ĐžĐŗĐž, ŅĐē ĐŧŅ–ĐēŅ€ĐžŅ„ĐžĐŊ СаŅ„Ņ–ĐēŅŅƒŅ” Ņ‚иŅˆŅƒ ĐŋŅ€ĐžŅ‚ŅĐŗĐžĐŧ ĐŋĐĩвĐŊĐžĐŗĐž Ņ‡Đ°ŅŅƒ + НадŅĐ¸ĐģĐ°Ņ‚и ĐŊĐ°ĐŗĐ°Đ´ŅƒĐ˛Đ°ĐŊĐŊŅ Ņ‚Đ° ĐŋĐĩŅ€ĐĩŅ…ОдиŅ‚и в Ņ€ĐĩĐļиĐŧ ŅĐŊŅƒ ĐŋĐĩŅ€ĐĩĐ´ ŅĐŊĐžĐŧ. ĐŖ СаĐŋĐģĐ°ĐŊОваĐŊиК Ņ‡Đ°Ņ ĐŋŅ€ĐžĐąŅƒĐ´ĐļĐĩĐŊĐŊŅ ĐŋŅ€ĐžĐģŅƒĐŊĐ°Ņ” ĐąŅƒĐ´Đ¸ĐģŅŒĐŊиĐē. + ПŅ€ĐžĐŗŅ€ĐĩŅ Са 7 Đ´ĐŊŅ–в + ОĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ + БоŅ€ĐžŅ‚ŅŒĐąĐ° + Xiaomi Watch S1 Active + ĐŖ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ĐŊĐĩĐŧĐ°Ņ” вŅ–ĐģŅŒĐŊиŅ… ŅĐģĐžŅ‚Ņ–в Đ´ĐģŅ ĐĩĐēŅ€Đ°ĐŊŅ–в вŅ–Đ´ĐļĐĩŅ‚Ņ–в (ŅƒŅŅŒĐžĐŗĐž ŅĐģĐžŅ‚Ņ–в: %1$s) + Ви Đ´Ņ–ĐšŅĐŊĐž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģиŅ‚и \'%1$s\'? + ВидаĐģиŅ‚и ĐĩĐēŅ€Đ°ĐŊ вŅ–Đ´ĐļĐĩŅ‚Đ° + ВŅ–Đ´ĐļĐĩŅ‚ + ОŅ‚Ņ€Đ¸ĐŧŅƒĐ˛Đ°Ņ‚и ŅĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ, ĐēĐžĐģи ви Đ´ĐžŅŅĐŗĐģи ĐŧĐ°ĐēŅĐ¸ĐŧĐ°ĐģŅŒĐŊĐžŅ— ĐēŅ–ĐģŅŒĐēĐžŅŅ‚Ņ– йаĐģŅ–в ĐļиŅ‚Ņ‚Ņ”вОŅ— ĐĩĐŊĐĩŅ€ĐŗŅ–Ņ— Са Đ´ĐĩĐŊŅŒ + ЕĐēŅ€Đ°ĐŊ вŅ–Đ´ĐļĐĩŅ‚Đ° + ЊОдĐĩĐŊĐŊиК ĐŋŅ€ĐžĐŗŅ€ĐĩŅ + МаĐēĐĩŅ‚ вŅ–Đ´ĐļĐĩŅ‚Đ° + НĐĩвŅ–Đ´ĐžĐŧиК Ņ‚иĐŋ Ņ‚Ņ€ĐĩĐŊŅƒĐ˛Đ°ĐŊĐŊŅ - %s + ПĐĩŅ€ĐĩĐŧŅ–ŅŅ‚иŅ‚и виŅ‰Đĩ + ІĐŊŅŅ‚Ņ€ŅƒĐŧĐĩĐŊŅ‚и ĐŊавŅ–ĐŗĐ°Ņ†Ņ–Ņ— + НаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°Ņ‚и ĐŋОвĐĩĐ´Ņ–ĐŊĐēŅƒ ĐŊавŅ–ĐŗĐ°Ņ†Ņ–ĐšĐŊĐžĐŗĐž СаŅŅ‚ĐžŅŅƒĐŊĐēŅƒ ĐŊĐ° ĐŗОдиĐŊĐŊиĐēŅƒ + Чи ĐŋОвиĐŊĐĩĐŊ ĐŊавŅ–ĐŗĐ°Ņ†Ņ–ĐšĐŊиК СаŅŅ‚ĐžŅŅƒĐŊĐžĐē авŅ‚ĐžĐŧĐ°Ņ‚иŅ‡ĐŊĐž вŅ–Đ´ĐēŅ€Đ¸Đ˛Đ°Ņ‚иŅŅ ĐŋОвĐĩŅ€Ņ… Ņ–ĐŊŅˆĐ¸Ņ… вŅ–ĐēĐžĐŊ, ĐēĐžĐģи вŅ–ĐŊ ĐžŅ‚Ņ€Đ¸ĐŧŅƒŅ” ĐŊавŅ–ĐŗĐ°Ņ†Ņ–ĐšĐŊŅ– вĐēаСŅ–вĐēи + 2 вŅ–Đ´ĐļĐĩŅ‚и + 1 вĐŗĐžŅ€Ņ–, 2 вĐŊиСŅƒ + 2 вĐŗĐžŅ€Ņ–, 2 вĐŊиСŅƒ + МаŅ” ĐąŅƒŅ‚и ĐŋŅ€Đ¸ĐŊĐ°ĐšĐŧĐŊŅ– %1$s ĐĩĐēŅ€Đ°ĐŊŅ–в + ВŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚и ĐžŅ‚Ņ€Đ¸ĐŧавŅˆĐ¸ ĐŊОвŅƒ вĐēаСŅ–вĐēŅƒ + ПĐĩŅ€ĐĩĐŧŅ–ŅŅ‚иŅ‚и вĐŊиС + 1 вŅ–Đ´ĐļĐĩŅ‚ + ПĐĩŅ€ĐĩĐšŅ‚и ĐŋОвĐĩŅ€Ņ… ŅƒŅŅ–Ņ… вŅ–ĐēĐžĐŊ + ПŅ–Đ´Ņ‚иĐŋ вŅ–Đ´ĐļĐĩŅ‚Đ° + ЕĐēŅ€Đ°ĐŊ %s + ВибĐĩŅ€Ņ–Ņ‚ŅŒ ŅƒŅŅ– вŅ–Đ´ĐļĐĩŅ‚и + 2 вĐŗĐžŅ€Ņ–, 1 вĐŊиСŅƒ + Чи ĐŋОвиĐŊĐĩĐŊ ĐŗОдиĐŊĐŊиĐē вŅ–ĐąŅ€ŅƒĐ˛Đ°Ņ‚и Са ĐēĐžĐļĐŊĐžŅŽ ĐŊОвОŅŽ айО СĐŧŅ–ĐŊĐĩĐŊĐžŅŽ ĐŊавŅ–ĐŗĐ°Ņ†Ņ–ĐšĐŊĐžŅŽ вĐēаСŅ–вĐēĐžŅŽ (Ņ‚Ņ–ĐģŅŒĐēи ĐēĐžĐģи СаŅŅ‚ĐžŅŅƒĐŊĐžĐē ĐŋĐžĐēаСаĐŊĐž ĐŋОвĐĩŅ€Ņ… Ņ–ĐŊŅˆĐ¸Ņ… вŅ–ĐēĐžĐŊ) + Назва ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ + Redmi Watch 2 Lite + Redmi Smart Band Pro + Nothing Ear (2) + ЛĐĩĐŗĐēĐĩ Đ°ĐēŅ‚ивĐŊĐĩ ŅˆŅƒĐŧĐžĐŋĐžĐŗĐģиĐŊĐ°ĐŊĐŊŅ + ĐĄŅ‚Đ°ĐŊ С\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ + Nothing Ear (Stick) + ПŅ€ĐžĐˇĐžŅ€Ņ–ŅŅ‚ŅŒ + ЗаĐŊиСŅŒĐēиК Ņ€Ņ–вĐĩĐŊŅŒ СаŅ€ŅĐ´Ņƒ Đ°ĐēŅƒĐŧŅƒĐģŅŅ‚ĐžŅ€Đ° ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅŽ + ЛиŅˆĐĩ ŅĐēŅ‰Đž ввŅ–ĐŧĐēĐŊĐĩĐŊĐž Đ°ĐēŅ‚ивŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋŅ–Đ´ĐŊĐĩŅĐĩĐŊĐŊŅĐŧ Ņ€ŅƒĐēи + ПаŅ€ĐžĐģŅŒ ĐŋОвиĐŊĐĩĐŊ ŅĐēĐģĐ°Đ´Đ°Ņ‚иŅŅ С 4 Ņ†Đ¸Ņ„Ņ€, Ņ– ĐŧŅ–ŅŅ‚иŅ‚и ĐģиŅˆĐĩ Ņ†Đ¸Ņ„Ņ€Đ¸ + ПовŅ‚ĐžŅ€ĐŊĐž С\'Ņ”Đ´ĐŊĐ°Ņ‚иŅŅ ĐģиŅˆĐĩ С ĐŋŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊиĐŧи ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅĐŧи, СаĐŧŅ–ŅŅ‚ŅŒ С\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ С ŅƒŅŅ–ĐŧĐ° ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅĐŧи + ĐĄĐŋОвŅ–Ņ‰ĐĩĐŊĐŊŅ ĐŊĐ° ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ— ĐŋŅ–ŅĐģŅ вŅ–Đ´\'Ņ”Đ´ĐŊĐ°ĐŊĐŊŅ вŅ–Đ´ BT. + 155 ŅƒĐ´/Ņ…в + 165 ŅƒĐ´/Ņ…в + 175 ŅƒĐ´/Ņ…в + 185 ŅƒĐ´/Ņ…в + 195 ŅƒĐ´/Ņ…в + 205 ŅƒĐ´/Ņ…в + Bluetooth Classic + ПовŅ‚ĐžŅ€ĐŊĐž ĐŋŅ–Đ´\'Ņ”Đ´ĐŊŅƒĐ˛Đ°Ņ‚иŅŅŒ ĐģиŅˆĐĩ Đ´Đž ĐŋОв\'ŅĐˇĐ°ĐŊиŅ… ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—в + Bluetooth LE \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 075291d05..3c4c9400c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -970,7 +970,7 @@ åšŗ 圈 æŗŗåŧ - SWOLF į´ĸåŧ• + SWOLF åšŗ均每圈æ­Ĩäŧ åšŗ均æ­Ĩäŧ åšŗå‡čĄŒį¨‹čˇįĻģ @@ -1329,7 +1329,7 @@ 取æļˆé€‰æ‹Šæ‰€æœ‰įš„åē”į”¨ äŊŋį”¨åē”į”¨åˆ—襨č‡ŗâ€Ļ æļˆæ¯éšį§æ¨Ąåŧ - 昞į¤ē内厚 + 昞į¤ē全部内厚 åē”į”¨åˆ—襨 åē”į”¨åŋ…éĄģčĸĢ选中才čƒŊčĸĢ配įŊŽ åē”į”¨åŋ…éĄģæœĒčĸĢ选中才čƒŊčĸĢ配įŊŽ @@ -1337,7 +1337,7 @@ 单ä¸Ēåē”į”¨čŽžåŽš 拒įģé€‰ä¸­åē”į”¨įš„通įŸĨ å…čŽ¸é€‰ä¸­åē”į”¨įš„通įŸĨ - éšč—å†…åŽš + éšč—å…¨éƒ¨å†…åŽš Galaxy Buds äŊŽéŸŗåĸžåŧē čŊ¯ @@ -2271,8 +2271,8 @@ %d 分钟 每周 PAI +%d - æ€ģ莥 PAI - 每æ—Ĩ AAI åĸžé•ŋ + æ€ģ莥 + 每æ—Ĩåĸžé•ŋ 需čĻ Catima æĨįŽĄį†äŧšå‘˜åĄ Catima 包名 厉čŖ… Catima @@ -2399,4 +2399,408 @@ æŗ•č¯­ īŧˆæŗ•å›Ŋīŧ‰ čŽžå¤‡æ˛Ąæœ‰čļŗ够įš„可į”¨įŠē间 发送文æœŦåˆ°čŽžå¤‡ + %1$d įš„åŊ“前 MTU å¤ĒäŊŽīŧŒč¯ˇåœ¨čŽžå¤‡čŽžįŊŽä¸­å¯į”¨éĢ˜ MTU åšļ断åŧ€/重新čŋžæŽĨčŽžå¤‡ã€‚ + 小įąŗ手įŽ¯ HRX + 启į”¨æ–°įš„发įŽ°æ´ģ动īŧŒčŋ™åē”č¯Ĩ可äģĨč§Ŗå†ŗčŽžå¤‡å‘įŽ°įš„é—Žéĸ˜ã€‚åĻ‚果您在搜į´ĸčŽžå¤‡æˆ–ä¸ŽčŽžå¤‡é…å¯šæ—ļ遇到äģģäŊ•é—Žéĸ˜īŧŒč¯ˇįĻį”¨æ­¤åŠŸčƒŊ。 + 启į”¨æ–°įš„发įŽ°æ´ģ动 + æĨį”ĩ莞įŊŽ + 启动æ´ģåŠ¨čŽĄæ—ļ器 + 莞įŊŽå›žæ ‡å§‹įģˆæ˜žį¤ē在æœĢå°ž + 三æŦĄ + Qrio åˇ˛é”åŽš + å…ŗæœēæ—ļ间 + įŸ­ + åĄčˇ¯é‡Œ + åˇĻ + 无 LED + 减速 + æ™ēčƒŊ振动 + Riiiver + å…čŽ¸ Wena 厚期čĻæą‚ Gadgetbridge äģŽčŽžå¤‡ä¸‹čŊŊæ´ģ动数捎 + čĨŋį“œåĄäŊ™éĸ + 中 + æ´ģåŠ¨čŽžįŊŽ + čŋžįģ­ + 莥æ­Ĩ器 + Qrio + į™Ŋ + åŊ“前æ—ļ间 + 后台æ´ģ动数捎同æ­Ĩ + åŧą + ä¸ģåąåš•å›žæ ‡ + įŠļ态栏中įš„夊气 + 加į´§ + åŗ + åŸēæœŦ + 昞į¤ē莞įŊŽ + 更大įš„å­—äŊ“ + čēĢäŊ“įƒ­é‡ + 两æŦĄ + čŽžå¤‡å°†æŒ‰į…§æŒ‡åŽšįš„æ—ļ间表č‡Ē动å…ŗæœē和åŧ€æœē + åŊ“您įœ‹å‘æ‰‹č…•æ—ļ打åŧ€æ˜žį¤ēåą + åĸžåŠ æ—Ĩ历、通įŸĨį­‰ä¸­įš„å­—äŊ“大小。 + 双å‡ģ + åŋĢ速 + Qrio åˇ˛č§Ŗ锁 + 一æŦĄ + æŽĨæ”ļæĨį”ĩ通įŸĨ + 在ä¸ģåąåš•åˇĻä¸Šč§’æ˜žį¤ēåŊ“前įŠļå†ĩ回标 + Wena Pay + 按钎动äŊœ + æĸ复æ—ļ间 + æĨį”ĩ振动 + įģŋ + éģ„ + į´ĸå°ŧ Wena 3 + æĨį”ĩ LED éĸœč‰˛ + įēĸ + 青 + č­ĻįŦ› + čœå•å›žæ ‡ + 重复æĨį”ĩ振动 + 通įŸĨ振动 + 无限期 + 四æŦĄ + 振动åŧēåēĻ + åĻ‚æžœå…ŗ闭īŧŒæ‚¨å°†ä¸äŧšåœ¨ Wena 上æ”ļ到æĨį”ĩ通įŸĨ + é•ŋ按 + äģ˜æŦž + įŠļ态éĄĩéĸ排åē + 闹钟设įŊŽ + 重复通įŸĨ振动 + 中 + 在ä¸ģåąåš•å›žæ ‡å‘¨å›´æˇģåŠ åœ†č§’įŸŠåŊĸ + 通įŸĨ LED éĸœč‰˛ + č‡Ē动å…ŗæœē + Edy äŊ™éĸ + č­Ļ告 + æ™ēčƒŊ闹钟čŖ•åēĻ + äŊŋį”¨ä¸°å¯Œįš„莞莥 + 一夊åŧ€å§‹äēŽ + 每ä¸Ēåē”į”¨į¨‹åēįš„通įŸĨ莞įŊŽ + 无振动 + į´Ģ + åŧē + 三倍 + 蓝 + æ´ģåŠ¨åąåš• + 通įŸĨ莞įŊŽ + 摄氏åēĻ + 渊åēĻæ ‡č¯† + é€‰æ‹ŠčŽžå¤‡æ˜¯äŊŋį”¨æ‘„氏åēĻčŋ˜æ˜¯åŽæ°åēĻ。 + 华氏 + Sony WF-1000XM5 + äģ…éšč—ä¸ģäŊ“ + æ‹‰č„ąįģ´äēšč¯­ + 常į”¨įŦĻåˇ + åŋŊį•ĨåˇĨäŊœčĩ„料通įŸĨ + 不čĻå°†åˇĨäŊœé…įŊŽæ–‡äģļ中įš„åē”į”¨į¨‹åēįš„通įŸĨå‘é€åˆ°æ‰‹čĄ¨ + æ™Žé€šæ¨Ąåŧīŧˆ60č‡ŗ90į§’īŧ‰ + åŋĢé€Ÿæ¨Ąåŧīŧˆ30į§’īŧ‰ + čˇƒæˆ‘ Balance + æĩ‹é‡æ¨Ąåŧ + į˛žįĄŽæ¨Ąåŧīŧˆ3分钟īŧ‰ + Femometer Vinca II + įē¯į™Ŋ + 最后通į‰’ + 速åēĻ䚋城 + č€ŗæœē + č‡Ēį”ąįģ„合 + čēĢäŊ“构成 + Zepp 支äģ˜ + 星įŠē + æĩŠį€šįš„夊įŠē + é”ģį‚ŧåŋĢæˇæ–šåŧ + é—Ēį”ĩ + åē”į”¨åŋĢæˇæ–šåŧ + 指å¯ŧ + 准备įŠļ态 + 渊åēĻ莥 + åšŗ均å‡ģįƒčˇįĻģ + į  + į›´įēŋ/分钟 + 最大å‡ģįƒčˇįĻģ + é“čˇ¯é•ŋåēĻ + æ€ģ莥å‡ģįƒ + å¯ŧčˆĒåˇ˛å¯åŠ¨īŧŒäŊ†æ‰‹čĄ¨ä¸ŠæœĒ厉čŖ…å¯ŧčˆĒåē”į”¨į¨‹åēã€‚č¯ˇäģŽåē”į”¨į¨‹åēįŽĄį†å™¨åŽ‰čŖ…厃。 + æ‰‹čĄ¨ä¸ŠæœĒ厉čŖ…å¯ŧčˆĒåē”į”¨ + åŋŊį•Ĩ īŧˆé™éŸŗīŧ‰ + 拒įģ + æĨį”ĩ拒æŽĨæ–šæŗ• + æ­Ŗåœ¨čŽˇå–įģŸčŽĄæ•°æŽ + 拒æŽĨæ‰‹čĄ¨æĨį”ĩæ—ļ采取äģ€äšˆæ“äŊœ + æ­Ŗåœ¨čŽˇå–æ¸ŠåēĻ数捎 + čˇƒæˆ‘æ´ģåŠ¨čžšį•Œ + čĄ€æ°§åšŗ均å€ŧ + čˇƒæˆ‘æ´ģ动 + å¯ŧčˆĒåē”į”¨ + å¯ŧčˆĒéĻ–选饚 + č°ˇæ­Œåœ°å›ž + 在通įŸĨ标éĸ˜å‰æˇģ加æĨč‡Ēåē”į”¨åį§°įš„前įŧ€ + 选拊į”¨äēŽčĻčŋžæŽĨįš„ OsmAnd į‰ˆæœŦ + 通įŸĨ中įš„åē”į”¨å + OsmAnd 包名 + OsmAnd(+) + å…ļäģ–通įŸĨ提醒 + æ—Ĩ历通įŸĨ提醒 + æĨį”ĩ提醒īŧˆæŒ¯åŠ¨/é¸ŖåĢīŧ‰ + 小įąŗ手įŽ¯8 + 跑æ­Ĩ + įĄįœ æ¨ĄåŧčŽĄåˆ’ + æĨį”ĩ提醒 + įĄįœ  + į”ĩ子邎äģļ通įŸĨ提醒 + į”ĩ子邎äģļ通įŸĨ提醒īŧˆæŒ¯åŠ¨/é¸ŖåĢīŧ‰ + 专æŗ¨ + SMSīŧˆįŸ­äŋĄīŧ‰é€šįŸĨįš„提醒īŧˆæŒ¯åŠ¨/é¸ŖåĢīŧ‰ + 在标éĸ˜ä¸­æ˜žį¤ēæļˆæ¯įš„éĸ„č§ˆ + įēĸįąŗæ‰‹čĄ¨3 čŋåŠ¨į‰ˆ + æ—Ĩ历通įŸĨ提醒īŧˆæŒ¯åŠ¨/é¸ŖåĢīŧ‰ + åēåˆ—åˇ + 业éēĻč¯­ + čĩˇåēŠ + įģŸčŽĄ + 针寚å…ļäģ–įąģåˆĢįš„通įŸĨ发å‡ē提醒īŧˆæŒ¯åŠ¨/é¸ŖåĢīŧ‰ + 小įąŗæ‰‹čĄ¨é’æ˜Ĩį‰ˆ + åœ¨čŽžå¤‡å…čŽ¸įš„情å†ĩ下īŧŒåœ¨é€šįŸĨ标éĸ˜ä¸­æ˜žį¤ēæļˆæ¯éĸ„č§ˆ + 发å‡ē一ä¸Ē提醒åšļåœ¨å°ąå¯æ—ļ间čŋ›å…ĨįĄįœ æ¨Ąåŧã€‚ 在éĸ„厚įš„čĩˇåēŠæ—ļ间īŧŒčĩˇåēŠé—šé’Ÿå°†äŧšå“čĩˇã€‚ + įŸ­äŋĄé€šįŸĨ提醒 + 提醒 + 小įąŗæ™ēčƒŊ手įŽ¯ 7 Pro + 小įąŗæ‰‹čĄ¨ S1 čŋåŠ¨į‰ˆ + 小įąŗæ‰‹čĄ¨åŊŠč‰˛čŋåŠ¨į‰ˆ + Pixoo + å‘čŽžå¤‡å‘é€åē”į”¨é€šįŸĨ + 发送通įŸĨ + č¯ˇå‹ŋ打扰 - åˇ˛åŧ€å¯ + č¯ˇå‹ŋ打扰 - äģ…限闚钟 + č¯ˇå‹ŋ打扰 - äģ…限äŧ˜å…ˆ + æœĒ莞įŊŽ + č¯ˇå‹ŋ打扰 - å…ŗ闭 + äŊŠæˆ´æ–šåŧ + Pebbleīŧˆéž‹æ‰Ŗīŧ‰ + æŦĄčĻį›Žæ ‡ + åŊ“您įš„æ´ģ力å€ŧ在čŋ‡åŽģ 7 å¤Šå†…čžžåˆ° 30、60 或 100 æ—ļīŧŒæ‚¨äŧšæ”ļ到通įŸĨ + æ´ģ力å€ŧ + åŊ“æ‚¨čžžåˆ°åŊ“夊įš„最大æ´ģ力å€ŧæ—ļæ”ļ到通įŸĨ + įĢ™įĢ‹æ—ļ间 + æ´ģ动æ—ļ间 + 手įŽ¯īŧˆč…•å¸Ļīŧ‰ + 项链īŧˆéĸˆå¸Ļīŧ‰ + 每æ—Ĩčŋ›åēĻ + 7夊čŋ›åą• + 手æœē静éŸŗæ¨Ąåŧ + 晎通 / 振动 + 震动 / 静éŸŗ + įēĸįąŗæ™ēčƒŊ手įŽ¯2 + 晎通 / 静éŸŗ + čļŠé‡Žčˇ‘ + 小部äģļå¸ƒåą€ + č¯ĨčŽžå¤‡æ˛Ąæœ‰į”¨äēŽå°éƒ¨äģļåąåš•įš„įŠē闲äŊįŊŽīŧˆäŊįŊŽæ€ģ数īŧš%1$sīŧ‰ + æœĒįŸĨé”ģį‚ŧ - %s + 上į§ģ + 您įĄŽåŽščĻåˆ é™¤â€œ%1$s”吗īŧŸ + åž’æ­Ĩ + 2 ä¸Ē小部äģļ + 删除小部äģļåąåš• + 1 ä¸ĒéĄļ部īŧŒ2 ä¸Ēåē•éƒ¨ + 2 ä¸ĒéĄļ部īŧŒ2 ä¸Ēåē•éƒ¨ + åŋ…éĄģč‡ŗ少有 %1$s ä¸Ēåąåš• + 小部äģļ + 小部äģļåąåš• + 下į§ģ + 1 ä¸Ē小部äģļ + 小部äģļ子įąģ型 + åąåš• %s + č¯ˇé€‰æ‹Šæ‰€æœ‰å°éƒ¨äģļ + 2 ä¸ĒéĄļ部īŧŒ1 ä¸Ēåē•éƒ¨ + æ‘”č§’ + éĢ˜äšéĢ˜ 2023 + éĢ˜äšéĢ˜ 2021 + 拍手äēŽčĩˇåąåš•" + 再æŦĄæ‹æ‰‹å…ŗé—­åąåš•" + éēĻ克éŖŽæŖ€æĩ‹åˆ°é™éŸŗ一æŽĩæ—ļé—´åŽå°†åąåš•å…ŗ闭 + įąŗåŽļ渊æšŋåēĻäŧ æ„Ÿå™¨2 + å¯ŧčˆĒč¯´æ˜Ž + 配įŊŽæ‰‹čĄ¨ä¸Šå¯ŧčˆĒåē”į”¨įš„čĄŒä¸ē + å¯ŧčˆĒåē”į”¨æ”ļ到å¯ŧčˆĒ指äģ¤æ—ļ是åĻč‡Ē动čŋ›å…Ĩ前台 + 栚捎新指äģ¤æŒ¯åŠ¨ + æĨ到前台 + čŽžå¤‡åį§° + æ‰‹čĄ¨æ˜¯åĻåē”č¯Ĩ在每ä¸Ē新įš„或更攚įš„å¯ŧčˆĒ指äģ¤æ—ļ振动īŧˆäģ…åŊ“åē”į”¨į¨‹åēäŊäēŽå‰å°æ—ļīŧ‰ + įēĸįąŗæ‰‹čĄ¨2青æ˜Ĩį‰ˆ + įēĸįąŗæ™ēčƒŊ手įŽ¯Pro + čŋžæŽĨįŠļ态 + Nothing Ear (2) + Nothing Ear (Stick) + čŊģåēĻä¸ģ动降å™Ē + 通透åēĻ + čŖč€€æ‰‹įŽ¯4 + čŖč€€æ‰‹įŽ¯5 + 华ä¸ēč¯­éŸŗ手įŽ¯B6 + 不čĻå–æļˆé€‰ä¸­æ™ēčƒŊå”¤é†’å¤é€‰æĄ†ã€‚ + 厞æ—ļį›‘控您įš„įĄįœ č´¨é‡å’Œå‘ŧå¸æ¨Ąåŧã€‚ +\n分析您įš„įĄįœ æ¨Ąåŧåšļ准įĄŽč¯Šæ–­ 6 į§įĄįœ é—Žéĸ˜ã€‚ + 华ä¸ēæ‰‹čĄ¨GT 3 īŧˆProīŧ‰ + 启į”¨æŽĨåŦį”ĩč¯ + 启į”¨äģŽčŽžå¤‡æŽĨ受į”ĩč¯ + 启į”¨č‡Ē动åŋƒįŽ‡æĩ‹é‡ + 某äē›čŽžå¤‡é”™č¯¯åœ°åŖ°į§°ä¸æ”¯æŒæŸäē›é€‰éĄšã€‚æ— čŽēåĻ‚äŊ•īŧŒæ­¤čŽžįŊŽå¯į”¨äēŽå¯į”¨åŽƒäģŦ。 +\näŊŋį”¨éŖŽé™Šį”ąæ‚¨č‡Ē行æ‰ŋ担。 +\n阅č¯ģįģ´åŸē + åŧēåˆļæ™ēčƒŊæŠĨč­Ļ支持。 +\néŖŽé™Šč‡Ē担 + åŧēåˆļč¯ˇå‹ŋ打扰支持 + 重新č§Ŗ析é”ģį‚ŧ数捎 + åŊ“č“į‰™æ–­åŧ€æ—ļåœ¨čŽžå¤‡ä¸Ščŋ›čĄŒé€šįŸĨ。 + äģ…åŊ“æŠŦč…•å¯į”¨æ—ļæŋ€æ´ģ昞į¤ē + 不äŊŠæˆ´æ—ļ启į”¨č¯ˇå‹ŋ打扰 + 手动 + 全夊 + čŖč€€æ‰‹įŽ¯3 + čŖč€€æ‰‹įŽ¯6 + čŖč€€æ‰‹įŽ¯7 + 华ä¸ē手įŽ¯īŧˆAW70īŧ‰ + čŖč€€æ‰‹įŽ¯6 + čŖč€€æ‰‹įŽ¯7 + čŖč€€æ‰‹įŽ¯8 + 华ä¸ēæ‰‹čĄ¨GT + 华ä¸ē手įŽ¯4īŧˆProīŧ‰ + 华ä¸ēæ‰‹čĄ¨GT 2īŧˆProīŧ‰ + 华ä¸ēæ‰‹čĄ¨GT 2e + åˇĨäŊœæ¨Ąåŧ + 不čĻé€‰ä¸­æ™ēčƒŊå”¤é†’å¤é€‰æĄ†ã€‚ + 华ä¸ē TruSleepâ„ĸ + 攚善įĄįœ į›‘æĩ‹ + 启į”¨æ‹’įģæĨį”ĩ + 启į”¨æ‹’įģæĨč‡ĒčŽžå¤‡įš„å‘ŧåĢ + åŊ“č¯ˇå‹ŋ打扰处äēŽæ´ģ动įŠļ态æ—ļįĻį”¨æŸĨ扞我įš„手æœē + 启į”¨č‡ĒåŠ¨čĄ€æ°§æĩ‹é‡ + åŧēåˆļ选项 + åŧēåˆļæ™ēčƒŊ闹钟 + åŧēåˆļäŊŠæˆ´äŊįŊŽ + åŧēåˆļäŊŠæˆ´äŊįŊŽæ”¯æŒã€‚ +\néŖŽé™Šč‡Ē担 + åŧēåˆļč¯ˇå‹ŋ打扰支持。 +\néŖŽé™Šč‡Ē担 + åŋŊį•Ĩ唤醒启动įŠļ态 + 可čƒŊ有劊äēŽæ­ŖįĄŽįš„įĄįœ æŖ€æĩ‹ã€‚ 在æ—Ĩ常æ´ģåŠ¨č§†å›žä¸­įĢ‹åŗå¯č§ã€‚ + åŋŊį•Ĩ唤醒įģ“束įŠļ态 + 可čƒŊ有劊äēŽæ­ŖįĄŽįš„įĄįœ æŖ€æĩ‹ã€‚ 在æ—Ĩ常æ´ģåŠ¨č§†å›žä¸­įĢ‹åŗå¯č§ã€‚ + čŋ™åĒäŧšåœ¨æŸäē›æ›´æ–°åŽæ‰§čĄŒæŸäē›æ“äŊœ + 向华ä¸ēčŽžå¤‡å‘é€č°ƒč¯•č¯ˇæą‚ + č°ƒč¯•č¯ˇæą‚ + äģ…重新čŋžæŽĨåˆ°åˇ˛čŋžæŽĨįš„čŽžå¤‡ + äģ…重新čŋžæŽĨåˆ°åˇ˛čŋžæŽĨįš„čŽžå¤‡īŧŒč€Œä¸æ˜¯é‡æ–°čŋžæŽĨåˆ°æ‰€æœ‰čŽžå¤‡ + čŽžå¤‡į”ĩæą į”ĩ量čŋ‡äŊŽ + 蜂é¸Ŗ器åŧēåēĻ + 密į åŋ…éĄģä¸ē 4 äŊæ•°å­—īŧŒä¸”åĒčƒŊäŊŋį”¨æ•°å­— + įąŗåŽļ MHO-C303 + į‰ˆæœŦ 1 + 小įąŗæ™ēčƒŊ手įŽ¯ 8 Pro + åšŗéĸ距įĻģ + åčŽŽį‰ˆæœŦ + 小įąŗæ‰‹čĄ¨ S3 + į‰ˆæœŦ 2 + 小įąŗæ‰‹čĄ¨ S1 + į‰ˆæœŦ 3 + 小įąŗæ‰‹čĄ¨ S1 Pro + æ´ģ动äŋĄæ¯ + åĻ‚果您įš„čŽžå¤‡ä¸å“åē” GadgetbridgeīŧŒæ‚¨å¯äģĨå°č¯•åŧēåˆļčŋžæŽĨįąģ型 + æ­Ŗ在上äŧ čĄ¨į›˜â€Ļâ€Ļ + æ­Ŗ在上äŧ čĄ¨į›˜ + 襨į›˜åŽ‰čŖ…åˇ˛åŽŒæˆ + 襨į›˜åŽ‰čŖ…å¤ąč´Ĩ + åŧēåˆļčŋžæŽĨįąģ型 + č‡Ē动 + äŊŽåŠŸč€—č“į‰™ + įģå…¸č“į‰™ + CMF Watch Pro + 155 bpm + 165 bpm + 175 bpm + 185 bpm + 195 bpm + 205 bpm + åŧē力čŋåŠ¨åŋƒįŽ‡č­ĻæŠĨ阈å€ŧ + åŽ¤å†…čˇ‘æ­Ĩ + į™ģåąą + äē¤å‰čŽ­įģƒæœē + č‡Ēį”ąčŽ­įģƒ + čĩ›č‰‡ + 动感单čŊĻ + æĨŧæĸ¯č¸æ­Ĩæœē + åĨčēĢčŋåŠ¨ + įģŧ合åĨčēĢ + 功čƒŊ莭įģƒ + äŊ“åŠ›čŽ­įģƒ + 跆æ‹ŗ道 + čļŠé‡Žčˇ‘ + įŠē手道 + å‡ģ剑 + 剑道 + 单杠 + 双杠 + å†ˇå´ + äē¤å‰čŽ­įģƒ + äģ°å§čĩˇå + åĨčēĢ游戏 + 有氧čŋåŠ¨ + æģšåŠ¨ + äŋ¯å§æ’‘ + 战įģŗ + åŧ•äŊ“向上 + 木æŋ + 标æžĒ + čˇŗčŋœ + 哑铃 + 肚įšŽčˆž + įˆĩåŖĢ舞 + æ‹‰ä¸čˆž + 芭蕾舞 + éŖžé•– + 射įŽ­ + éĒ‘éŠŦ + 攞éŖŽį­ + 摇摆 + æĨŧæĸ¯ + 钓éąŧ + 手éĒ‘č‡Ē行čŊĻ + åŋƒįĩ和čēĢäŊ“ + åĄåˇ´čŋĒ + åĄä¸čŊĻ + 台įƒ + įžŊ毛įƒ + čē˛éŋįƒ + æžŗ大刊äēščļŗįƒ + 匚克įƒ + æ›˛æŖįƒ + 射å‡ģ + å¸†čˆščŋåŠ¨ + æģ‘æ°´ + į´ĸå°ŧ WI-SP600N + æ­Ļ术 + å˛å¯†æ–¯æœē + įĩæ´ģ性 + į”°åž„ + 在一æŽĩæ—ļ间内įĻį”¨čĄĨ充水分č­Ļ告 + čˇŗéĢ˜ + å…ļäģ–čˆžčšˆ + å¤Ē极 + éŖžį›˜čŋåŠ¨ + čšĻåēŠ + 冰įƒ + čŊŽæģ‘ + å‘ŧå•Ļ圈 + 垒įƒ + æēœå†° + 冰åŖļ + é›Ē上čŋåŠ¨ + 打įŒŽ + æˆˇå¤–æ•Ŗæ­Ĩ + čļŠé‡Žæģ‘é›Ē + æģ‘æŋčŋåŠ¨ + æ”€å˛Š + čŖ耀 MagicWatch 2 + įēĸįąŗæ‰‹čĄ¨2 \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 6b5a520c6..6b07c1605 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1,5 +1,9 @@ + + + + @string/pref_theme_system @string/pref_theme_light @@ -157,6 +161,17 @@ buttons_on_right + + @string/wearmode_band + @string/wearmode_pebble + @string/wearmode_necklace + + + band + pebble + necklace + + @string/horizontal @string/vertical @@ -189,6 +204,17 @@ @string/p_alarm_clock + + @string/silent_mode_normal_vibrate + @string/silent_mode_normal_silent + @string/silent_mode_vibrate_silent + + + normal_vibrate + normal_silent + vibrate_silent + + Pebble Health Misfit @@ -271,6 +297,11 @@ @string/mi2_dnd_automatic @string/mi2_dnd_scheduled + + @string/mi2_dnd_off + @string/dnd_all_day + @string/mi2_dnd_scheduled + @string/p_off @string/p_automatic @@ -770,6 +801,16 @@ @string/zepp_os_watchface_emerald_moonlight @string/zepp_os_watchface_rotating_earth @string/zepp_os_watchface_superposition + + @string/zepp_os_watchface_vast_sky + @string/zepp_os_watchface_lightning_flash + @string/zepp_os_watchface_free_combination + @string/zepp_os_watchface_pure_white + @string/zepp_os_watchface_guider + @string/zepp_os_watchface_city_of_speed + @string/zepp_os_watchface_starry_sky + @string/zepp_os_watchface_the_ultima + @@ -784,6 +825,14 @@ emerald_moonlight rotating_earth superposition + vast_sky + lightning_flash + free_combination + pure_white + guider + city_of_speed + starry_sky + the_ultima @@ -830,6 +879,12 @@ @string/menuitem_cards @string/menuitem_mi_ai @string/menuitem_zepp_coach + @string/menuitem_zepp_pay + @string/menuitem_readiness + @string/menuitem_body_composition + @string/menuitem_thermometer + @string/menuitem_workout_shortcuts + @string/menuitem_apps_shortcuts @string/menuitem_more @@ -878,6 +933,12 @@ cards mi_ai zepp_coach + zepp_pay + readiness + body_composition + thermometer + workout_shortcuts + apps_shortcuts more @@ -909,6 +970,12 @@ @string/menuitem_stopwatch @string/menuitem_recommendation @string/menuitem_zepp_coach + @string/menuitem_zepp_pay + @string/menuitem_readiness + @string/menuitem_body_composition + @string/menuitem_thermometer + @string/menuitem_workout_shortcuts + @string/menuitem_apps_shortcuts @string/menuitem_alexa @@ -939,6 +1006,12 @@ stopwatch recommendation zepp_coach + zepp_pay + readiness + body_composition + thermometer + workout_shortcuts + apps_shortcuts alexa @@ -958,6 +1031,8 @@ @string/menuitem_lockscreen @string/menuitem_findphone @string/menuitem_eject_water + @string/menuitem_headphone + @string/menuitem_buzzer_intensity @@ -976,6 +1051,8 @@ lockscreen findphone eject_water + headphone + buzzer_intensity @@ -1124,6 +1201,237 @@ indoor_ice_skating + + @string/activity_type_indoor_running + @string/activity_type_outdoor_running + @string/activity_type_outdoor_walking + @string/activity_type_indoor_walking + @string/activity_type_outdoor_cycling + @string/activity_type_indoor_cycling + @string/activity_type_mountain_hike + @string/activity_type_hiking + @string/activity_type_cross_trainer + @string/activity_type_free_training + @string/activity_type_strength_training + @string/activity_type_yoga + @string/activity_type_boxing + @string/activity_type_rower + @string/activity_type_dynamic_cycle + @string/activity_type_stair_stepper + @string/activity_type_treadmill + @string/activity_type_hiit + @string/activity_type_fitness_exercises + @string/activity_type_jump_roping + @string/activity_type_pilates + @string/activity_type_crossfit + @string/activity_type_functional_training + @string/activity_type_physical_training + @string/activity_type_taekwondo + @string/activity_type_cross_country_running + @string/activity_type_karate + @string/activity_type_fencing + @string/activity_type_core_training + @string/activity_type_kendo + @string/activity_type_horizontal_bar + @string/activity_type_parallel_bar + @string/activity_type_cooldown + @string/activity_type_cross_training + @string/activity_type_sit_ups + @string/activity_type_fitness_gaming + @string/activity_type_aerobic_exercise + @string/activity_type_rolling + @string/activity_type_flexibility + @string/activity_type_gymnastics + @string/activity_type_track_and_field + @string/activity_type_push_ups + @string/activity_type_battle_rope + @string/activity_type_smith_machine + @string/activity_type_pull_ups + @string/activity_type_plank + @string/activity_type_javelin + @string/activity_type_long_jump + @string/activity_type_high_jump + @string/activity_type_trampoline + @string/activity_type_dumbbell + @string/activity_type_belly_dance + @string/activity_type_jazz_dance + @string/activity_type_latin_dance + @string/activity_type_ballet + @string/activity_type_street_dance + @string/activity_type_zumba + @string/activity_type_other_dance + @string/activity_type_roller_skating + @string/activity_type_martial_arts + @string/activity_type_tai_chi + @string/activity_type_hula_hooping + @string/activity_type_disc_sports + @string/activity_type_darts + @string/activity_type_archery + @string/activity_type_horse_riding + @string/activity_type_kite_flying + @string/activity_type_swing + @string/activity_type_stairs + @string/activity_type_fishing + @string/activity_type_hand_cycling + @string/activity_type_mind_and_body + @string/activity_type_wrestling + @string/activity_type_kabaddi + @string/activity_type_karting + @string/activity_type_badminton + @string/activity_type_table_tennis + @string/activity_type_tennis + @string/activity_type_billiards + @string/activity_type_bowling + @string/activity_type_volleyball + @string/activity_type_shuttlecock + @string/activity_type_handball + @string/activity_type_baseball + @string/activity_type_softball + @string/activity_type_cricket + @string/activity_type_rugby + @string/activity_type_hockey + @string/activity_type_squash + @string/activity_type_dodgeball + @string/activity_type_soccer + @string/activity_type_basketball + @string/activity_type_australian_football + @string/activity_type_golf + @string/activity_type_pickleball + @string/activity_type_lacross + @string/activity_type_shot + @string/activity_type_sailing + @string/activity_type_surfing + @string/activity_type_jet_skiing + @string/activity_type_skating + @string/activity_type_ice_hockey + @string/activity_type_curling + @string/activity_type_snowboarding + @string/activity_type_cross_country_skiing + @string/activity_type_snow_sports + @string/activity_type_skiing + @string/activity_type_skateboarding + @string/activity_type_rock_climbing + @string/activity_type_hunting + + + + indoor_running + outdoor_running + outdoor_walking + indoor_walking + outdoor_cycling + indoor_cycling + mountain_hike + hiking + cross_trainer + free_training + strength_training + yoga + boxing + rower + dynamic_cycle + stair_stepper + treadmill + hiit + fitness_exercises + jump_roping + pilates + crossfit + functional_training + physical_training + taekwondo + cross_country_running + karate + fencing + core_training + kendo + horizontal_bar + parallel_bar + cooldown + cross_training + sit_ups + fitness_gaming + aerobic_exercise + rolling + flexibility + gymnastics + track_and_field + push_ups + battle_rope + smith_machine + pull_ups + plank + javelin + long_jump + high_jump + trampoline + dumbbell + belly_dance + jazz_dance + latin_dance + ballet + street_dance + zumba + other_dance + roller_skating + martial_arts + tai_chi + hula_hooping + disc_sports + darts + archery + horse_riding + kite_flying + swing + stairs + fishing + hand_cycling + mind_and_body + wrestling + kabaddi + karting + badminton + table_tennis + tennis + billiards + bowling + volleyball + shuttlecock + handball + baseball + softball + cricket + rugby + hockey + squash + dodgeball + soccer + basketball + australian_football + golf + pickleball + lacross + shot + sailing + surfing + jet_skiing + skating + ice_hockey + curling + snowboarding + cross_country_skiing + snow_sports + skiing + skateboarding + rock_climbing + hunting + + + + indoor_run + outdoor_run + + @string/activity_type_outdoor_running @string/activity_type_hiking @@ -2041,6 +2349,16 @@ @string/timeformat_am_pm + + active_time + standing_time + + + + @string/active_time + @string/standing_time + + @string/p_timeformat_auto @string/p_timeformat_24h @@ -2106,6 +2424,10 @@ @string/japanese @string/korean @string/hebrew + @string/danish + @string/norwegian_bokmal + @string/romanian + @string/swedish @@ -2140,6 +2462,10 @@ ja_JP ko_KO he_IL + da_DK + nb_NO + ro_RO + sv_SE @@ -2294,6 +2620,26 @@ 150 + + @string/off + @string/heartrate_bpm_155 + @string/heartrate_bpm_165 + @string/heartrate_bpm_175 + @string/heartrate_bpm_185 + @string/heartrate_bpm_195 + @string/heartrate_bpm_205 + + + + 0 + 155 + 165 + 175 + 185 + 195 + 205 + + @string/off @string/heartrate_bpm_40 @@ -2502,7 +2848,6 @@ - @string/pref_button_action_disabled @string/pref_media_play @string/pref_media_pause @string/pref_media_playpause @@ -2510,10 +2855,13 @@ @string/pref_device_action_fitness_app_control_start @string/pref_device_action_fitness_app_control_stop @string/pref_device_action_fitness_app_control_toggle + @string/pref_device_action_dnd_off + @string/pref_device_action_dnd_priority + @string/pref_device_action_dnd_alarms + @string/pref_device_action_dnd_on - @string/pref_button_action_disabled_value @string/pref_media_play_value @string/pref_media_pause_value @string/pref_media_playpause_value @@ -2521,6 +2869,10 @@ @string/pref_device_action_fitness_app_control_start_value @string/pref_device_action_fitness_app_control_stop_value @string/pref_device_action_fitness_app_control_toggle_value + @string/pref_device_action_dnd_off_value + @string/pref_device_action_dnd_priority_value + @string/pref_device_action_dnd_alarms_value + @string/pref_device_action_dnd_on_value @@ -2641,6 +2993,7 @@ @string/menuitem_pai @string/stats_title @string/liveactivity_live_activity + @string/pref_header_spo2 @@ -2653,6 +3006,7 @@ @string/p_pai @string/p_speed_zones @string/p_live_stats + @string/p_spo2 @@ -2665,6 +3019,7 @@ @string/p_pai @string/p_speed_zones @string/p_live_stats + @string/p_spo2 @string/p_temperature @@ -2724,8 +3079,15 @@ 2 3 - - + + + @string/prefs_active_noise_cancelling + @string/prefs_active_noise_cancelling_light + @string/prefs_active_noise_cancelling_transparency + @string/off + + + anc anc-light transparency @@ -2909,6 +3271,20 @@ ambient_sound + + @string/automatic + @string/sony_protocol_v1 + @string/sony_protocol_v2 + @string/sony_protocol_v3 + + + + auto + v1 + v2 + v3 + + @string/sony_sound_position_off @string/sony_sound_position_front @@ -3070,6 +3446,29 @@ de.dennisguse.opentracks.nightly + + @string/automatic + net.osmand.plus + net.osmand + net.osmand.dev + + + autodetect + net.osmand.plus + net.osmand + net.osmand.dev + + + + @string/automatic + @string/manual + + + + auto + manual + + @string/arabic @string/bengali @@ -3093,6 +3492,7 @@ @string/scandinavian @string/turkish @string/ukranian + @string/hungarian @@ -3118,6 +3518,7 @@ scandinavian turkish ukranian + hungarian @@ -3423,5 +3824,23 @@ normal precise + + @string/call_rejection_method_reject + @string/call_rejection_method_ignore + + + reject + ignore + + + @string/pref_force_connection_type_auto + @string/pref_force_connection_type_ble + @string/pref_force_connection_type_bt_classic + + + @string/pref_force_connection_type_auto_value + @string/pref_force_connection_type_ble_value + @string/pref_force_connection_type_bt_classic_value + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c42ca7044..90ea767f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -203,7 +203,9 @@ Time Workout Equalizer - Connect to Gadgetbridge device when Bluetooth is turned on + Connect to Gadgetbridge device(s) when Bluetooth is turned on + Reconnect only to connected devices + Reconnect only to connected devices, instead of reconnecting to all devices Start automatically Reconnect automatically Broadcast Media Button Intents Directly @@ -332,10 +334,24 @@ Send calendar events to the timeline Show device specific notification icon Show a device specific Android notification icon instead the Gadgetbridge icon when connected + Show a preview of the message in the title + Shows a preview of the message in the title of a notification as allowed by the device + Alert for calendar notifications + Alert (vibrate/beep) for calendar notifications + Alert for incoming calls + Alert (vibrate/beep) for incoming calls + Alert for email notifications + Alert (vibrate/beep) for email notifications + Alert for SMS notifications + Alert (vibrate/beep) for SMS (text message) notifications + Alert for "other" notifications + Alert (vibrate/beep) for notifications in the "other" category Autoremove dismissed notifications Notifications are automatically removed from the device when dismissed from the phone Screen On on Notifications Turn on the band\'s screen when a notification arrives + Send notifications + Send app notifications to the device Privacy mode Normal notifications Shift the notification text off-screen @@ -394,6 +410,7 @@ Units Time format Screen on duration + Secondary goal All day heart rate measurement HPlus/Makibes settings @@ -478,6 +495,10 @@ Beep once Beep twice Vibrate and beep once + + Clap hands to turn up screen + Clapping again will turn off the screen + The screen will turn off after the microphone has detected silence for a while Device specific settings Auth Key @@ -604,6 +625,9 @@ Vertical Buttons on left Buttons on right + Band (wristband) + Pebble (shoe buckle) + Necklace (neck strap) No valid user data given, using dummy user data for now. When your Mi Band vibrates and blinks, tap it a few times in a row. Install @@ -630,9 +654,12 @@ Fetching SpO2 data Fetching heart rate data Fetching sleep respiratory rate data + Fetching temperature data + Fetching statistics From %1$s to %2$s Wearing left or right? Wearing direction + Wearing mode Vibration profile Default Staccato @@ -685,6 +712,12 @@ 140 bpm 145 bpm 150 bpm + 155 bpm + 165 bpm + 175 bpm + 185 bpm + 195 bpm + 205 bpm 80% 85% 90% @@ -758,6 +791,7 @@ Vibrate the band when the heart rate is over a threshold, without any obvious physical activity in the last 10 minutes. This feature is experimental, and was not extensively tested. Heart rate alert threshold High heart rate alert threshold + High activity heart rate alert threshold Low heart rate alert threshold Stress monitoring Monitor stress level while resting @@ -831,6 +865,7 @@ Problem with the firmware transfer. DO NOT REBOOT your Mi Band! Problem with the firmware metadata transfer The device does not have enough free space + The device battery is too low Firmware installation complete Firmware installation complete, rebooting deviceâ€Ļ Firmware flashing failed @@ -917,6 +952,7 @@ Time Disconnect notification + Notification on device when disconnected from BT. Button actions Specify button press actions Button press count @@ -942,21 +978,30 @@ The band will vibrate when you have been inactive for a while Inactivity threshold (in minutes) Disable inactivity warnings for a time interval + Disable hydration warnings for a time interval Heart Rate Monitoring Configure heart rate monitoring + Phone Silent Mode + Normal / Vibrate + Normal / Silent + Vibrate / Silent Always On Display Style follows Watchface Style Keep the band\'s display always on + Device name Password Lock the band with a password when removed from the wrist Password Enabled The password must have 4 digits, using numbers 1 to 4 + The password must have 4 digits, using only numbers The password must have 6 digits, using only numbers Configure heart rate monitoring and alert thresholds Start time End time Activate display upon lift during Do Not Disturb + Only if activate display upon lift enabled + Do not disturb when not wearing Band screen unlock" Swipe up to unlock the band\'s screen Night mode @@ -980,6 +1025,7 @@ The band will vibrate if your phone disconnects from the band Interface language Automatic + Manual Simplified Chinese Traditional Chinese English @@ -1031,6 +1077,7 @@ Hebrew Swedish Czech + Danish About to transfer data since %1$s Waiting for reconnect About you @@ -1078,11 +1125,14 @@ Heart rate Heart rate average Stress average + Blood oxygen average Daily target: calories burnt Daily target: distance in meters Daily target: active time in minutes Daily target: standing time in minutes Daily target: fat burn time in minutes + Active time + Standing time Store raw record in the database Stores the data \"as is\", increasing the database usage to allow for later interpretation. Data management @@ -1173,6 +1223,7 @@ At sunset Automatic (sleep detection) Scheduled (time interval) + All day Duration Attempting to pair with %1$s Bonding with %1$s failed immediately. @@ -1195,7 +1246,83 @@ Device not worn Running Outdoor Running + Indoor Running + Mountain Hike + Cross trainer + Free training + Rower + Dynamic cycle + Stair stepper + Fitness exercises + Crossfit + Functional training + Physical training + Taekwondo + Cross country running + Karate + Fencing + Kendo + Horizontal bar + Parallel bar + Cooldown + Cross training + Sit ups + Fitness gaming + Aerobic exercise + Rolling + Flexibility + Track and field + Push ups + Battle rope + Smith machine + Pull ups + Plank + Javelin + Long jump + High jump + Trampoline + Dumbbell + Belly dance + Jazz dance + Latin dance + Ballet + Other dance + Roller skating + Martial arts + Tai chi + Hula hooping + Disc sports + Darts + Archery + Horse riding + Kite Flying + Swing + Stairs + Fishing + Hand cycling + Mind and body + Kabaddi + Karting + Billiards + Shuttlecock + Softball + Dodgeball + Australian football + Pickleball + Lacross + Shot + Sailing + Jet skiing + Skating + Ice hockey + Curling + Cross country skiing + Snow sports + Skateboarding + Rock climbing + Hunting Walking + Outdoor Walking Indoor Walking Surfing Windsurfing @@ -1252,6 +1379,9 @@ Ice Skating Golfing Other + Trekking + Trail run + Wrestling Unknown activity Sport Activities Sport Activity Detail @@ -1294,7 +1424,12 @@ Mi Band 5 Mi Band 6 Xiaomi Smart Band 7 + Xiaomi Smart Band 7 Pro + Xiaomi Smart Band 8 + Xiaomi Smart Band 8 Pro Amazfit Balance + Amazfit Active + Amazfit Active Edge Amazfit Cheetah (Square) Amazfit Cheetah (Round) Amazfit Cheetah Pro @@ -1361,6 +1496,8 @@ iTag BFH-16 Mijia Smart Clock + Mijia Temperature and Humidity Sensor 2 + Mijia MHO-C303 Makibes HR3 Bangle.js TLW64 @@ -1369,6 +1506,8 @@ Wasp-os SMA-Q2 OSS FitPro + ColaCao 2021 + ColaCao 2023 Domyos T540 Sony WH-1000XM2 Sony WH-1000XM3 @@ -1378,9 +1517,32 @@ Sony WF-1000XM3 Sony WF-1000XM4 Sony WF-1000XM5 + Sony WI-SP600N Sony LinkBuds S Binary sensor + Honor Band 3 + Honor Band 4 + Honor Band 5 + Honor Band 6 + Honor Band 7 + Honor MagicWatch 2 + Huawei Band (AW70) + Huawei Band 6 + Huawei Band 7 + Huawei Band 8 + Huawei Watch GT + Huawei Band 4 (Pro) + Huawei Watch GT 2 (Pro) + Huawei Watch GT 2e + Huawei Talk Band B6 + Huawei Watch GT 3 (Pro) Femometer Vinca II + Xiaomi Watch Lite + Redmi Watch 3 Active + Redmi Smart Band 2 + Redmi Watch 2 + Redmi Watch 2 Lite + Redmi Smart Band Pro Choose export location General High-priority @@ -1407,6 +1569,12 @@ VO₂ Max Recommendation Zepp Coach + Zepp Pay + Thermometer + Readiness + Body composition + Workout shortcuts + Apps shortcuts Breathing Cycle Tracking Alarm @@ -1465,6 +1633,8 @@ Wi-Fi Lockscreen Eject Water + Headphone + Buzzer Intensity Unknown (%s) [UNSUPPORTED] %s Red Fantasy @@ -1477,6 +1647,14 @@ Emerald Moonlight Rotating Earth superposition + Vast Sky + Lightning flash + Free combination + Pure white + Guider + City of speed + Starry sky + The ultima Yesterday\'s Activity Minutes: Hours: @@ -1640,6 +1818,7 @@ Downhill Uphill distance Downhill distance + Flat distance Elevation gain Elevation loss Maximum @@ -1677,9 +1856,13 @@ Min Cadence Average Stroke Distance Average Strokes + Average Stroke Rate + Max Stroke Rate + Total Strokes Average Lap Pace - swolfIndex - swimStyle + SWOLF + Swim Style + Lane Length Laps Ascending Descending @@ -1689,6 +1872,7 @@ m cm + yard ft steps m/s @@ -1696,6 +1880,7 @@ mi/h min/mi str/s + str/min str sec swolf index @@ -1891,6 +2076,9 @@ Take measurements during sleep Frequency of measurements Nothing Ear (1) + Nothing Ear (2) + Nothing Ear (Stick) + CMF Watch Pro Galaxy Buds Galaxy Buds Live Galaxy Buds Pro @@ -1931,6 +2119,8 @@ Ambient Mode Ambient Sound Options Active Noise Cancelling + Light Active Noise Cancelling + Transparency Active Noise Cancelling Level High Low @@ -1984,8 +2174,8 @@ Mild Moderate High - PAI Total - Day PAI increase + Total + Day increase Mode Off Noise Cancelling @@ -2081,6 +2271,10 @@ Ambient Sound, Off Quick Access (Double Tap) Quick Access (Triple Tap) + Version 1 + Version 2 + Version 3 + Protocol Version Auto Brightness Adjust screen brightness according to ambient light Screen Brightness @@ -2117,6 +2311,10 @@ Fitness App Tracking Stop Toggle Fitness App Tracking GPS Location Listener Stop + Do not disturb - Off + Do not disturb - Priority only + Do not disturb - Alarms only + Do not disturb - On ###m @@ -2126,6 +2324,21 @@ ###ft + Work Mode + Do not uncheck smart wakeup checkbox. + Do not check smart wakeup checkbox. + HUAWEI TruSleep ™ + Monitor your sleep quality and breathing pattern in real time.\nAnalyze your sleep patterns and accurately diagnose 6 types of sleeping problems. + Improved sleep monitoring + Activity recognition settings + recognize running + recognize biking + recognize walking + recognize rowing + none + ask + auto + Menu Some buttons cannot be configured because their functions are hard-coded in the watch firmware.\n\nWarning: long-pressing the upper button when a watchface from the official Fossil app is installed will also toggle between showing/hiding widgets. OpenTracks package name @@ -2135,6 +2348,31 @@ pre-setting position to %s Light up on new notification + Enable accepting calls + Enable accepting calls from the device + Enable rejecting calls + Enable rejecting calls from the device + + Disable find my phone when do not disturb is active + + Enable automatic heartrate measuring + Enable automatic SpO2 measuring + + Force options + Some devices falsely claim not to have support for some options. This settings can be used to enable them anyway.\nUSE AT YOUR OWN RISK\nRead the wiki + Force smart alarm + Force smart alarms support.\nUSE AT YOUR OWN RISK + Force wear location + Force wear location support.\nUSE AT YOUR OWN RISK + Force Do Not Disturb support + Force Do Not Disturb support.\nUSE AT YOUR OWN RISK + Ignore wakeup start status + May help with proper sleep detection. Visible immediately in the daily activities view. + Ignore wakeup end status + May help with proper sleep detection. Visible immediately in the daily activities view. + "Reparse workout data" + "This will only do something after certain updates" + no devices connected %d devices connected Set parent folder @@ -2189,6 +2427,8 @@ Turbo Speed Lights Blinking + Send a debug request to Huawei device + Debug request AsteroidOS SoFlow SO6 Lock @@ -2361,9 +2601,82 @@ Siren Short No LED - Temperature scale Select whether device uses Celsius or Fahrenheit scale. Celsius Fahrenheit + Navigation app not installed on watch + Navigation started but navigationApp not installed on watch. Please install it from the App Manager. + Reject + Ignore (silence) + Call rejection method + Which action is taken when an incoming call is rejected from the watch + App name in notification + Prefix notification title with name of source application + OsmAnd package name + Used for selecting the version of OsmAnd to connect to + Navigation preferences + Navigation apps + OsmAnd(+) + Google Maps + Serial Number + Stats + Running + Alerts + Focus + Bedtime + Wake Up + Sleep Mode Schedule + Send a reminder and enter sleep mode at bedtime. At the scheduled wake-up time, the wake-up alarm will sound. + Xiaomi Watch S1 + Xiaomi Watch S3 + Xiaomi Watch S1 Active + Xiaomi Watch S1 Pro + Mi Watch Color Sport + Pixoo + Not set + Vitality Score + 7-day progress + Get a notification when your vitality score reaches 30, 60 or 100 in the past 7 days + Daily progress + Get a notification when you reached the maximum number of vitality points for the day + Widget + Widget Screen + Delete widget screen + Are you sure you want to delete \'%1$s\'? + The device has no free slots for widget screens (total slots: %1$s) + There must be a minimum of %1$s screens + 1 top, 2 bottom + 2 top, 1 bottom + 2 top, 2 bottom + Widget layout + Widget Subtype + Screen %s + 1 widget + 2 widgets + Move up + Move down + Please select all widgets + Unknown workout - %s + Navigation instructions + Configure on-watch navigation app behavior + Come to foreground + Whether the navigation app should automatically come to the foreground when it receives a navigation instruction + Vibrate on new instruction + Whether the watch should vibrate on every new or changed navigation instruction (only when the app is in the foreground) + Connection Status + Uploading watchfaceâ€Ļ + Uploading watchface + Watchface installation completed + Watchface installation failed + Force connection type + You may try forcing the connection type in case your device does not respond to Gadgetbridge + Automatic + Bluetooth LE + Bluetooth Classic + BOTH + BLE + BT_CLASSIC + Activity info + Could not post ongoing notification due to missing permission diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index 5ef21f6c8..979d1a8de 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -105,6 +105,7 @@ pai speedzones livestats + spo2 temperature off @@ -125,6 +126,10 @@ FITNESS_CONTROL_START FITNESS_CONTROL_STOP FITNESS_CONTROL_TOGGLE + DO_NOT_DISTURB_OFF + DO_NOT_DISTURB_PRIORITY + DO_NOT_DISTURB_ALARMS + DO_NOT_DISTURB_ON system light diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 84dbd2343..0b3fc33d4 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,140 @@ + + Initial support for Honor Magic Watch 2 + Initial support for Mijia MHO-C303 + Initial support for Nothing CMF Watch Pro + Initial support for Sony WI-SP600N + Experimental support for Redmi Watch 2 + Experimental support for Xiaomi Smart Band 8 Pro + Experimental support for Xiaomi Watch S1 Pro + Experimental support for Xiaomi Watch S1 + Experimental support for Xiaomi Watch S3 + Galaxy Buds2 Pro: Fix recognition of some versions + Huawei Watch GT 2: Fix pairing + Redmi Smart Band Pro: Fix password digits + Pebble: Fix app configuration page + Pebble 2: Fix pairing issue + PineTime: Fix weather forecast on InfiniTime's new simple weather + Xiaomi: Fix sleep sometimes extending past the wakeup time + Xiaomi: Request battery level and charging state periodically + Xiaomi: Fix sleep stage parsing for some devices + Zepp OS: Improve device discovery + Zepp OS: Fix weather not working on some devices + Zepp OS: Prevent crash when installing large firmware updates + Fix sport activity summary group order + Fix reconnection to devices failing occasionally + + + Initial support for Honor Band 3,4,5,6 + Initial support for Huawei Band 4, 4 Pro, 6, 7, 3e, 4e + Initial support for Huawei Talk Band B6 + Initial support for Huawei Watch GT, GT 2 + Initial support for Mijia LYWSD03MMC + Initial support for Nothing Ear (2) + Initial support for Nothing Ear (Stick) + Experimental support for Honor Band 7 + Experimental support for Redmi Watch 2 Lite + Experimental support for Redmi Smart Band Pro + Casio GBX100: Add support for snooze alarm + Fossil/Skagen Hybrids: Update navigationApp to 1.1 + Huami: Fetch SpO2 on devices that support it + Pebble: Attempt to fix app configuration webview + PineTime: Add support for InfiniTime's new simple weather + PineTime: Fix freeze and reboot when upgrading firmware + Pixoo: Enable sending images (non-persistent) + Pixoo: Get and send alarms + Pixoo: Set custom device name + Pixoo: support "clap hands to turn off screen" and "sleep after silence" settings + Xiaomi: Improve activity and workout parsing + Xiaomi: Improve stability and fix some crashes + Xiaomi: Improve weather + Xiaomi: Parse sleep stages + Add a notifications channel for connection status notifications + Improve automatic connection to all or previous devices + Fix devices sometimes staying stuck in a "Connecting" state + Map some missing Google Maps navigation actions + + + Initial support for Amazfit Balance + Initial support for Amazfit Active + Initial support for ColaCao 2021 + Initial support for ColaCao 2023 + Initial support for Femometer Vinca II + Initial support for Mijia LYWSD02MMC variant + Initial support for Sony Wena 3 + Experimental support for Divoom Pixoo + Experimental support for Sony WF-1000XM5 + Experimental support for Amazfit Active Edge + Experimental support for Mi Band 7 Pro (Xiaomi Smart Band 7 Pro) + Experimental support for Mi Band 8 (Xiaomi Smart Band 8) + Experimental support for Mi Watch Lite + Experimental support for Mi Watch Color Sport + Experimental support for Redmi Smart Band 2 + Experimental support for Redmi Watch 3 Active + Experimental support for Xiaomi Watch S1 Active + Amazfit Band 7: Add alexa menu entries + Amazfit GTR 3 Pro: Fix firmware and watchface upload + Amazfit T-Rex: Fix activity summary parsing + Amazfit T-Rex Pro: Add activate display on lift sensitivity + AsteroidOS: Add more supported watch models + AsteroidOS: Fix media info + AsteroidOS: Fix notification dismissal + Bangle.js: Add loyalty cards integration with Catima + Bangle.js: Ensure SMS messages have src field set to "SMS Message" + Bangle.js: Fix GPS speed + Bangle.js: Improve handling of chinese characters + Bangle.js: Lower threshold for low battery warning + Bangle.js: Recover from device initialization failure + Casio GBX100/GBD-200: Fix first connect + Casio GB5600/6900/STB-1000: Fix pairing + Casio GDB-200: Fix notification timestamp + Casio GDB-200: Fixed notification categories and default category + Casio GDB-200: Allow preview of notification message alongside title + Casio GDB-200: Fixed find my phone feature + Intent API: Add debug action for test new function + Fossil/Skagen Hybrids: Add new navigation app + Fossil/Skagen Hybrids: Allow configuring call rejection method + Fossil/Skagen Hybrids: Fix some preference crashes on the nightly + Fossil/Skagen Hybrids: Reduce toasts on release builds + Fossil/Skagen Hybrids: Show device specific settings in more logical order + Huami: Toggle phone silent mode from band + Message privacy: Add mode Hide only body + Mijia LYWSD02: Add battery + Mijia LYWSD02: Add low battery notification + Mijia LYWSD02: Set temperature unit + Mijia LYWSD02: Fix battery drain while connected + PineTime: Display app name for VoIP app calls + PineTime: Honor Sync time setting on connect + PineTime: Improve notification handling + PineTime: Reduce weather memory usage + Withings Steel HR: Fix crash when calibrating hands on the nightly + Zepp OS: Add blood oxygen graph + Zepp OS: Add workout codes for hiking and outdoor swimming + Zepp OS: Allow disabling app notifications per device + Zepp OS: Attempt to fix activity fetch operation getting stuck + Zepp OS: Display swimming activity data + Zepp OS: Fix health settings on older Zepp OS versions + Zepp OS: Fix setting of unknown button press apps + Zepp OS: Fix sunrise and moon dates being off by local time + UTC offset + Zepp OS: Map hiking, outdoor swimming, climbing and table tennis activity types + Zepp OS: Toggle phone silent mode from band + Add transliteration for Latvian, Hungarian, Common Symbols + Allow multiple device actions to be triggered for the same event + Allow toggling DND through device actions + Autodetect OsmAnd package name and make it configurable + Improve ASCII transliterator + Make GMaps navigation handler follow the "navigation forwarding" setting + Support selecting enabled navigation apps + Allow ignore notifications from work profile apps + Display alias in low battery notification + Fix crash when pairing current device as companion + Fix emoji when a transliterator is enabled + Fix UV Index and rain probability for some weather apps + Improve device discovery stability and fix freezes + Improve Telegram and COL Reminder notifications + Replace old-style preference switch with Material 3 switch + Amazfit GTR Mini: Mark as not experimental Bangle.js: Improve file downloads diff --git a/app/src/main/res/xml/devicesettings_activity_info_header.xml b/app/src/main/res/xml/devicesettings_activity_info_header.xml new file mode 100644 index 000000000..5b281033c --- /dev/null +++ b/app/src/main/res/xml/devicesettings_activity_info_header.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_allow_accept_reject_calls.xml b/app/src/main/res/xml/devicesettings_allow_accept_reject_calls.xml new file mode 100644 index 000000000..f233e9471 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_allow_accept_reject_calls.xml @@ -0,0 +1,15 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_autoremove_notifications.xml b/app/src/main/res/xml/devicesettings_autoremove_notifications.xml index 0ecb6d6aa..682b7156c 100644 --- a/app/src/main/res/xml/devicesettings_autoremove_notifications.xml +++ b/app/src/main/res/xml/devicesettings_autoremove_notifications.xml @@ -6,4 +6,4 @@ android:key="autoremove_notifications" android:summary="@string/pref_summary_autoremove_notifications" android:title="@string/pref_title_autoremove_notifications" /> - \ No newline at end of file + diff --git a/app/src/main/res/xml/devicesettings_canned_dismisscall_16.xml b/app/src/main/res/xml/devicesettings_canned_dismisscall_16.xml index 8722b5640..5436114d5 100644 --- a/app/src/main/res/xml/devicesettings_canned_dismisscall_16.xml +++ b/app/src/main/res/xml/devicesettings_canned_dismisscall_16.xml @@ -13,6 +13,11 @@ android:summary="@string/pref_summary_canned_messages_set" android:title="@string/pref_title_canned_messages_set" /> + + diff --git a/app/src/main/res/xml/devicesettings_canned_reply_16.xml b/app/src/main/res/xml/devicesettings_canned_reply_16.xml index 50e2455d8..5d1790f78 100644 --- a/app/src/main/res/xml/devicesettings_canned_reply_16.xml +++ b/app/src/main/res/xml/devicesettings_canned_reply_16.xml @@ -20,6 +20,11 @@ android:title="@string/pref_title_canned_reply_suffix" app:useSimpleSummaryProvider="true" /> + + diff --git a/app/src/main/res/xml/devicesettings_casio_alert.xml b/app/src/main/res/xml/devicesettings_casio_alert.xml new file mode 100644 index 000000000..c5327fe6a --- /dev/null +++ b/app/src/main/res/xml/devicesettings_casio_alert.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_device_actions.xml b/app/src/main/res/xml/devicesettings_device_actions.xml index 39ca0a2d4..db353cfc6 100644 --- a/app/src/main/res/xml/devicesettings_device_actions.xml +++ b/app/src/main/res/xml/devicesettings_device_actions.xml @@ -12,12 +12,11 @@ android:title="@string/prefs_events_forwarding_fellsleep" android:icon="@drawable/ic_nights_stay"> - - - - - + + + diff --git a/app/src/main/res/xml/devicesettings_disable_find_phone_with_dnd.xml b/app/src/main/res/xml/devicesettings_disable_find_phone_with_dnd.xml new file mode 100644 index 000000000..6dc02369b --- /dev/null +++ b/app/src/main/res/xml/devicesettings_disable_find_phone_with_dnd.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_donotdisturb_allday_liftwirst.xml b/app/src/main/res/xml/devicesettings_donotdisturb_allday_liftwirst.xml new file mode 100644 index 000000000..1109a700c --- /dev/null +++ b/app/src/main/res/xml/devicesettings_donotdisturb_allday_liftwirst.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_force_connection_type.xml b/app/src/main/res/xml/devicesettings_force_connection_type.xml new file mode 100644 index 000000000..3b584e03f --- /dev/null +++ b/app/src/main/res/xml/devicesettings_force_connection_type.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_force_options.xml b/app/src/main/res/xml/devicesettings_force_options.xml new file mode 100644 index 000000000..8b1e3ba89 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_force_options.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr_all_fw.xml similarity index 76% rename from app/src/main/res/xml/devicesettings_fossilhybridhr.xml rename to app/src/main/res/xml/devicesettings_fossilhybridhr_all_fw.xml index 2d0d49a05..13b70bb60 100644 --- a/app/src/main/res/xml/devicesettings_fossilhybridhr.xml +++ b/app/src/main/res/xml/devicesettings_fossilhybridhr_all_fw.xml @@ -114,15 +114,6 @@ - - - - - - - - - - - - + android:icon="@drawable/baseline_merge_24" + android:title="@string/pref_title_fossil_hr_navigation_instructions" + android:summary="@string/pref_summary_fossil_hr_navigation_instructions" + android:key="navigation_app_config"> - - - - + android:key="fossil_hr_nav_auto_foreground" + android:title="@string/pref_title_fossil_hr_nav_foreground" + android:summary="@string/pref_summary_fossil_hr_nav_foreground" + android:icon="@drawable/ic_info"/> + diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr_dev.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr_dev.xml new file mode 100644 index 000000000..b0d83c628 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_fossilhybridhr_dev.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr_buttonconfiguration.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr_post_fw300.xml similarity index 100% rename from app/src/main/res/xml/devicesettings_fossilhybridhr_buttonconfiguration.xml rename to app/src/main/res/xml/devicesettings_fossilhybridhr_post_fw300.xml diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr_pre_fw20.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr_pre_fw220.xml similarity index 100% rename from app/src/main/res/xml/devicesettings_fossilhybridhr_pre_fw20.xml rename to app/src/main/res/xml/devicesettings_fossilhybridhr_pre_fw220.xml diff --git a/app/src/main/res/xml/devicesettings_fossilhybridhr_buttonconfiguration_pre_fw30.xml b/app/src/main/res/xml/devicesettings_fossilhybridhr_pre_fw300.xml similarity index 86% rename from app/src/main/res/xml/devicesettings_fossilhybridhr_buttonconfiguration_pre_fw30.xml rename to app/src/main/res/xml/devicesettings_fossilhybridhr_pre_fw300.xml index 30cb3110a..9c6015046 100644 --- a/app/src/main/res/xml/devicesettings_fossilhybridhr_buttonconfiguration_pre_fw30.xml +++ b/app/src/main/res/xml/devicesettings_fossilhybridhr_pre_fw300.xml @@ -54,5 +54,14 @@ android:key="button_1_function_long_warning" android:summary="@string/fossil_hr_button_config_info" /> - + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_goal_secondary.xml b/app/src/main/res/xml/devicesettings_goal_secondary.xml new file mode 100644 index 000000000..bfb1c47d0 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_goal_secondary.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_heartrate_automatic_enable.xml b/app/src/main/res/xml/devicesettings_heartrate_automatic_enable.xml new file mode 100644 index 000000000..8bed9f4a5 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_heartrate_automatic_enable.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_heartrate_sleep_alert_activity_stress_spo2.xml b/app/src/main/res/xml/devicesettings_heartrate_sleep_alert_activity_stress_spo2.xml index b2a84fc37..2cbe5135c 100644 --- a/app/src/main/res/xml/devicesettings_heartrate_sleep_alert_activity_stress_spo2.xml +++ b/app/src/main/res/xml/devicesettings_heartrate_sleep_alert_activity_stress_spo2.xml @@ -70,6 +70,15 @@ android:key="heartrate_alert_low_threshold" android:summary="%s" android:title="@string/prefs_heartrate_alert_low_threshold" /> + + diff --git a/app/src/main/res/xml/devicesettings_huawei.xml b/app/src/main/res/xml/devicesettings_huawei.xml new file mode 100644 index 000000000..d0c8a8011 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_huawei.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_hydration_reminder_dnd.xml b/app/src/main/res/xml/devicesettings_hydration_reminder_dnd.xml new file mode 100644 index 000000000..a0295022e --- /dev/null +++ b/app/src/main/res/xml/devicesettings_hydration_reminder_dnd.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_inactivity_sheduled.xml b/app/src/main/res/xml/devicesettings_inactivity_sheduled.xml new file mode 100644 index 000000000..bc98c8ad8 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_inactivity_sheduled.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_nothing_ear1.xml b/app/src/main/res/xml/devicesettings_nothing_ear1.xml index 746624935..05dd62534 100644 --- a/app/src/main/res/xml/devicesettings_nothing_ear1.xml +++ b/app/src/main/res/xml/devicesettings_nothing_ear1.xml @@ -8,8 +8,8 @@ android:title="@string/nothing_prefs_inear_title" /> diff --git a/app/src/main/res/xml/devicesettings_phone_silent_mode.xml b/app/src/main/res/xml/devicesettings_phone_silent_mode.xml new file mode 100644 index 000000000..1866d1036 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_phone_silent_mode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_pixoo.xml b/app/src/main/res/xml/devicesettings_pixoo.xml new file mode 100644 index 000000000..6120c78cb --- /dev/null +++ b/app/src/main/res/xml/devicesettings_pixoo.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_prefix_notification_with_app.xml b/app/src/main/res/xml/devicesettings_prefix_notification_with_app.xml new file mode 100644 index 000000000..4398bb873 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_prefix_notification_with_app.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_preview_message_in_title.xml b/app/src/main/res/xml/devicesettings_preview_message_in_title.xml new file mode 100644 index 000000000..cfd18189b --- /dev/null +++ b/app/src/main/res/xml/devicesettings_preview_message_in_title.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_reject_call_method.xml b/app/src/main/res/xml/devicesettings_reject_call_method.xml new file mode 100644 index 000000000..c9b9d8a78 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_reject_call_method.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_send_app_notifications.xml b/app/src/main/res/xml/devicesettings_send_app_notifications.xml new file mode 100644 index 000000000..e6b5dab84 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_send_app_notifications.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_sleep_mode_schedule.xml b/app/src/main/res/xml/devicesettings_sleep_mode_schedule.xml new file mode 100644 index 000000000..8dce9b59c --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sleep_mode_schedule.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_device_info.xml b/app/src/main/res/xml/devicesettings_sony_headphones_device_info.xml index 0997efcfb..6bfc4c275 100644 --- a/app/src/main/res/xml/devicesettings_sony_headphones_device_info.xml +++ b/app/src/main/res/xml/devicesettings_sony_headphones_device_info.xml @@ -14,5 +14,14 @@ android:title="@string/audio_codec" app:useSimpleSummaryProvider="true" /> + + diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_protocol_version.xml b/app/src/main/res/xml/devicesettings_sony_headphones_protocol_version.xml new file mode 100644 index 000000000..c9e1d7337 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_protocol_version.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/xml/devicesettings_spo_automatic_enable.xml b/app/src/main/res/xml/devicesettings_spo_automatic_enable.xml new file mode 100644 index 000000000..f59cd0705 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_spo_automatic_enable.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_trusleep.xml b/app/src/main/res/xml/devicesettings_trusleep.xml new file mode 100644 index 000000000..7a65510a3 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_trusleep.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_vibrationpatterns.xml b/app/src/main/res/xml/devicesettings_vibrationpatterns.xml index 6e244f6a5..ec0acaa6d 100644 --- a/app/src/main/res/xml/devicesettings_vibrationpatterns.xml +++ b/app/src/main/res/xml/devicesettings_vibrationpatterns.xml @@ -1,5 +1,6 @@ - + + android:key="vibration_profile_key_app_alerts" + android:persistent="false" + android:title="@string/pref_screen_notification_profile_generic"> @@ -23,27 +24,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_app_alerts" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_incoming_call" + android:persistent="false" + android:title="@string/pref_screen_notification_profile_incoming_call"> @@ -53,27 +55,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_incoming_call" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_incoming_sms" + android:persistent="false" + android:title="@string/pref_screen_notification_profile_sms"> @@ -83,27 +86,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_incoming_sms" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_goal_notification" + android:persistent="false" + android:title="@string/mi2_prefs_goal_notification"> @@ -113,27 +117,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_goal_notification" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_alarm" + android:persistent="false" + android:title="@string/vibration_profile_alarm_clock"> @@ -143,27 +148,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_alarm" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_idle_alerts" + android:persistent="false" + android:title="@string/pref_screen_notification_idle_alerts"> @@ -173,27 +179,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_idle_alerts" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_event_reminder" + android:persistent="false" + android:title="@string/pref_screen_notification_profile_event_reminder"> @@ -203,27 +210,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_event_reminder" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_find_band" + android:persistent="false" + android:title="@string/pref_screen_notification_profile_find_device"> @@ -233,27 +241,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_find_band" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_todo_list" + android:persistent="false" + android:title="@string/pref_screen_notification_profile_todo_list"> @@ -263,27 +272,28 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_todo_list" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> + android:key="vibration_profile_key_schedule" + android:persistent="false" + android:title="@string/pref_screen_notification_profile_schedule"> @@ -293,20 +303,21 @@ android:entries="@array/vibration_profile" android:entryValues="@array/vibration_profile_values" android:key="huami_vibration_profile_schedule" - android:title="@string/miband_prefs_vibration" - android:summary="%s" /> + android:summary="%s" + android:title="@string/miband_prefs_vibration" /> + android:title="@string/pref_title_notifications_repetitions" + app:useSimpleSummaryProvider="true" /> + android:persistent="false" + android:title="@string/vibration_try" /> diff --git a/app/src/main/res/xml/devicesettings_vitality_score.xml b/app/src/main/res/xml/devicesettings_vitality_score.xml new file mode 100644 index 000000000..eb109d7fc --- /dev/null +++ b/app/src/main/res/xml/devicesettings_vitality_score.xml @@ -0,0 +1,23 @@ + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_wearmode.xml b/app/src/main/res/xml/devicesettings_wearmode.xml new file mode 100644 index 000000000..7584dd982 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_wearmode.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_widgets.xml b/app/src/main/res/xml/devicesettings_widgets.xml new file mode 100644 index 000000000..5c137f993 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_widgets.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_workmode.xml b/app/src/main/res/xml/devicesettings_workmode.xml new file mode 100644 index 000000000..51edc5dbf --- /dev/null +++ b/app/src/main/res/xml/devicesettings_workmode.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/xml/devicesettings_workout_activity_types.xml b/app/src/main/res/xml/devicesettings_workout_activity_types.xml new file mode 100644 index 000000000..5213d34e2 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_workout_activity_types.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_xiaomi_displayitems.xml b/app/src/main/res/xml/devicesettings_xiaomi_displayitems.xml new file mode 100644 index 000000000..1ca0247c7 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_xiaomi_displayitems.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index c6f9a2b05..e8451b3d4 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -18,6 +18,13 @@ android:layout="@layout/preference_checkbox" android:title="@string/pref_title_general_autoconnectonbluetooth" app:iconSpaceReserved="false" /> + - - + + + + + + + + + . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; + +public class TestHuaweiCrypto { + + private void printByteArrayAsHex(String name, byte[] array) { + System.out.print(name + ": ["); + for (int i = 0; i < array.length - 1; i++) { + System.out.print(Integer.toHexString(array[i] & 0xFF) + ", "); + } + System.out.println(Integer.toHexString(array[array.length - 1] & 0xFF) + "]"); + } + + private void printIntArray(String name, int[] array) { + System.out.print(name + ": ["); + for (int i = 0; i < array.length - 1; i++) { + System.out.print(array[i] + ", "); + } + System.out.println(array[array.length - 1] + "]"); + } + + @Test + public void testGenerateNonce() { + // The function output contains randomness, so we test multiple times + + // We also check how often each byte is present, and that the difference isn't too high + // Note that this may fail due to the randomness + int[] bytePresent = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + Assert.assertEquals(256, bytePresent.length); + + for (int i = 0; i < 1000; i++) { + byte[] output = HuaweiCrypto.generateNonce(); + + printByteArrayAsHex("Output", output); + + Assert.assertEquals(16, output.length); + + for (byte b : output) { + bytePresent[b & 0xFF] += 1; + } + } + + printIntArray("Bytes present", bytePresent); + + int minCount = Integer.MAX_VALUE; + int maxCount = Integer.MIN_VALUE; + for (int c : bytePresent) { + minCount = Math.min(c, minCount); + maxCount = Math.max(c, maxCount); + } + + // The limit here is quite arbitrary, erring on the high side + if (maxCount - minCount > 60) { + Assert.fail("Difference in byte counts is suspiciously high, check the randomness of the nonce."); + } + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestHuaweiPacket.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestHuaweiPacket.java new file mode 100644 index 000000000..a6b761242 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestHuaweiPacket.java @@ -0,0 +1,269 @@ +/* Copyright (C) 2023 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ + +package nodomain.freeyourgadget.gadgetbridge.devices.huawei; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FindPhone; + +public class TestHuaweiPacket { + + HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() { + + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return false; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + HuaweiPacket.ParamsProvider paramsProviderEncrypt = new HuaweiPacket.ParamsProvider() { + + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + HuaweiPacket.ParamsProvider paramsProviderSmallSlice = new HuaweiPacket.ParamsProvider() { + + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return false; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0x10; + } + }; + + @Test + public void testEmptyPacketParse() { + byte[] input = {}; + + HuaweiPacket packet = new HuaweiPacket(paramsProvider); + + try { + packet.parse(input); + } catch (HuaweiPacket.LengthMismatchException e) { + // Pass + } catch (HuaweiPacket.ParseException e) { + Assert.fail(); + } + } + + @Test + public void testUnknownUnencryptedPacketParse() throws HuaweiPacket.ParseException { + byte[] input = {0x5a, 0x00, 0x07, 0x00, 0x7f, 0x7f, 0x01, 0x02, 0x03, 0x04, 0x40, (byte) 0xb6}; + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, (short) 0x0304); + + HuaweiPacket packet = new HuaweiPacket(paramsProvider); + packet = packet.parse(input); + + Assert.assertEquals(HuaweiPacket.class, packet.getClass()); + Assert.assertEquals(0x7f, packet.serviceId); + Assert.assertEquals(0x7f, packet.commandId); + Assert.assertFalse(packet.isEncrypted); + Assert.assertTrue(packet.complete); + Assert.assertEquals(expectedTlv, packet.getTlv()); + } + + @Test + public void testUnknownEncryptedPacketParse() throws HuaweiPacket.ParseException { + byte[] input = {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x7f, (byte) 0x7f, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x9e, (byte) 0x40, (byte) 0xe1, (byte) 0xea, (byte) 0x15, (byte) 0xf6, (byte) 0x50, (byte) 0x80, (byte) 0x8c, (byte) 0x45, (byte) 0x19, (byte) 0xd5, (byte) 0x2a, (byte) 0xbb, (byte) 0x29, (byte) 0xb8, (byte) 0xD5, (byte) 0x24}; + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, (short) 0x0304); + + HuaweiPacket packet = new HuaweiPacket(paramsProvider); + packet = packet.parse(input); + + Assert.assertEquals(HuaweiPacket.class, packet.getClass()); + Assert.assertEquals(0x7f, packet.serviceId); + Assert.assertEquals(0x7f, packet.commandId); + Assert.assertTrue(packet.isEncrypted); + Assert.assertTrue(packet.complete); + Assert.assertEquals(expectedTlv, packet.getTlv()); + } + + @Test + public void testKnownEncryptedPacketParse() throws HuaweiPacket.ParseException { + byte[] input = {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x0b, (byte) 0x01, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x28, (byte) 0x00, (byte) 0x99, (byte) 0x6f, (byte) 0x2a, (byte) 0xcb, (byte) 0x62, (byte) 0x3a, (byte) 0xe6, (byte) 0x54, (byte) 0x28, (byte) 0x54, (byte) 0xf8, (byte) 0xab, (byte) 0x54, (byte) 0x83, (byte) 0xf4, (byte) 0xf4}; + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, false); + + HuaweiPacket packet = new HuaweiPacket(paramsProvider); + packet = packet.parse(input); + + Assert.assertEquals(FindPhone.Response.class, packet.getClass()); + Assert.assertEquals(0x0b, packet.serviceId); + Assert.assertEquals(0x01, packet.commandId); + Assert.assertTrue(packet.isEncrypted); + Assert.assertTrue(packet.complete); + Assert.assertEquals(expectedTlv, packet.getTlv()); + } + + @Test + public void testUnencryptedUnslicedSerialize() throws HuaweiPacket.CryptoException { + byte serviceId = 0x01; + byte commandId = 0x02; + HuaweiTLV tlv = new HuaweiTLV() + .put(0x03, 0x05060708); + + byte[] expected = {(byte) 0x5a, (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0xA4, (byte) 0xF0}; + + HuaweiPacket packet = new HuaweiPacket(paramsProvider); + packet.isSliced = false; + packet.isEncrypted = false; + packet.serviceId = serviceId; + packet.commandId = commandId; + packet.setTlv(tlv); + + List output = packet.serialize(); + + Assert.assertEquals(1, output.size()); + Assert.assertArrayEquals(expected, output.get(0)); + } + + @Test + public void testEncryptedUnslicedSerialize() throws HuaweiPacket.CryptoException { + byte serviceId = 0x01; + byte commandId = 0x02; + HuaweiTLV tlv = new HuaweiTLV() + .put(0x03, 0x05060708); + + byte[] expected = {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x3b, (byte) 0x89, (byte) 0xfc, (byte) 0x79, (byte) 0xd9, (byte) 0x05, (byte) 0x5e, (byte) 0xed, (byte) 0x52, (byte) 0x35, (byte) 0xfe, (byte) 0x16, (byte) 0xa0, (byte) 0x8a, (byte) 0x4d, (byte) 0x53, (byte) 0x93, (byte) 0xc7}; + + HuaweiPacket packet = new HuaweiPacket(paramsProviderEncrypt); + packet.isSliced = false; + packet.isEncrypted = true; + packet.serviceId = serviceId; + packet.commandId = commandId; + packet.setTlv(tlv); + + List output = packet.serialize(); + + Assert.assertEquals(1, output.size()); + Assert.assertArrayEquals(expected, output.get(0)); + } + + @Test + public void testUnencryptedSlicedSerialize() throws HuaweiPacket.CryptoException { + byte serviceId = 0x01; + byte commandId = 0x02; + HuaweiTLV tlv = new HuaweiTLV() + .put(0x01, 0x00) + .put(0x02, 0x00) + .put(0x03, 0x00) + .put(0x04, 0x00); + + byte[] expected1 = {(byte) 0x5a, (byte) 0x00, (byte) 0x0b, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0xcc, (byte) 0x98}; + byte[] expected2 = {(byte) 0x5a, (byte) 0x00, (byte) 0x0b, (byte) 0x02, (byte) 0x01, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0xfa, (byte) 0xd3}; + byte[] expected3 = {(byte) 0x5a, (byte) 0x00, (byte) 0x0a, (byte) 0x03, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x04, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x37, (byte) 0xca}; + + HuaweiPacket packet = new HuaweiPacket(paramsProviderSmallSlice); + packet.isSliced = true; + packet.isEncrypted = false; + packet.serviceId = serviceId; + packet.commandId = commandId; + packet.setTlv(tlv); + + List output = packet.serialize(); + + Assert.assertEquals(3, output.size()); + Assert.assertArrayEquals(expected1, output.get(0)); + Assert.assertArrayEquals(expected2, output.get(1)); + Assert.assertArrayEquals(expected3, output.get(2)); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestHuaweiTLV.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestHuaweiTLV.java new file mode 100644 index 000000000..02fcdb665 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestHuaweiTLV.java @@ -0,0 +1,532 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TestHuaweiTLV { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return false; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testSerializeEmpty() { + byte[] expectedOutput = {}; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + + Assert.assertArrayEquals(expectedOutput, huaweiTLV.serialize()); + } + + @Test + public void testSerialize() { + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {})); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x42})); + byte[] expectedOutput = {0x01, 0x00, 0x01, 0x01, 0x42}; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertArrayEquals(expectedOutput, huaweiTLV.serialize()); + } + + @Test + public void testPutEmptyTag() { + int tag = 0x01; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {})); + + HuaweiTLV huaweiTLV = new HuaweiTLV().put(tag); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutNullByteArray() { + int tag = 0x01; + byte[] input = null; + ArrayList expectedValueMap = new ArrayList<>(); + + //noinspection ConstantConditions + HuaweiTLV huaweiTLV = new HuaweiTLV().put(tag, input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutByteArray() { + int tag = 0x01; + byte[] input = {0x01, 0x02, 0x03}; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01, 0x02, 0x03})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag, input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutByte() { + int tag = 0x01; + byte input = 0x13; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x13})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag, input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutBooleans() { + int tag1 = 0x01; + int tag2 = 0x02; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01})); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x02, new byte[] {0x00})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag1, true) + .put(tag2, false); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutInt() { + int tag = 0x01; + int input = 0xDEADBEEF; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag, input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutShort() { + int tag = 0x01; + short input = (short) 0xCAFE; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xCA, (byte) 0xFE})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag, input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutString() { + int tag = 0x01; + String input = "Hello world!"; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV( + (byte) 0x01, + new byte[] {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21} + )); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag, input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutHuaweiTLV() { + int tag = 0x01; + HuaweiTLV input = new HuaweiTLV().put(0x01, (short) 0x1337); + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01, 0x02, 0x13, 0x37})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag, input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testPutMultipleEqualEmptyTags() { + int tag = 0x01; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {})); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .put(tag) + .put(tag); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testParseEmpty() { + byte[] input = {}; + ArrayList expectedValueMap = new ArrayList<>(); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .parse(input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testParseSingleByte() { + byte[] input = {0x01, 0x01, 0x01}; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .parse(input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testParseBytes() { + byte[] input = {0x01, 0x04, (byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF}; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .parse(input); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testParseZeroOffsetLength() { + byte[] input = {}; + ArrayList expectedValueMap = new ArrayList<>(); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .parse(input, 0, 0); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testParseMalformed() { + byte[] input = {(byte) 0x01, (byte) 0x01}; + new HuaweiTLV() + .parse(input); + Assert.fail(); + } + + @Test + public void testParseOffsetLength() { + byte[] input = {(byte) 0x90, (byte) 0x90, (byte) 0x90, 0x01, 0x00}; + int offset = 3; + int length = 2; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {})); + + HuaweiTLV huaweiTLV = new HuaweiTLV() + .parse(input, offset, length); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testParseWrongOffsetLength() { + byte[] input = {}; + new HuaweiTLV() + .parse(input, 1, 1); + Assert.fail(); + } + + @Test + public void testGetBytesEmpty() throws HuaweiPacket.MissingTagException { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {})); + byte[] expectedOutput = new byte[] {}; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertArrayEquals(expectedOutput, huaweiTLV.getBytes(tag)); + } + + @Test + public void testGetBytes() throws HuaweiPacket.MissingTagException { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01, 0x02, 0x03})); + byte[] expectedOutput = new byte[] {0x01, 0x02, 0x03}; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertArrayEquals(expectedOutput, huaweiTLV.getBytes(tag)); + } + + @Test + public void testGetByte() throws HuaweiPacket.MissingTagException { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x04})); + Byte expectedOutput = 0x04; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertEquals(expectedOutput, huaweiTLV.getByte(tag)); + } + + @Test + public void testGetBooleans() throws HuaweiPacket.MissingTagException { + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01})); + input.add(new HuaweiTLV.TLV((byte) 0x02, new byte[] {0x00})); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertEquals(true, huaweiTLV.getBoolean(0x01)); + Assert.assertEquals(false, huaweiTLV.getBoolean(0x02)); + } + + @Test + public void testGetInteger() throws HuaweiPacket.MissingTagException { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF})); + Integer expectedOutput = 0xDEADBEEF; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertEquals(expectedOutput, huaweiTLV.getInteger(tag)); + } + + @Test + public void testGetShort() throws HuaweiPacket.MissingTagException { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xCA, (byte) 0xFE})); + Short expectedOutput = (short) 0xCAFE; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertEquals(expectedOutput, huaweiTLV.getShort(tag)); + } + + @Test + public void testGetString() throws HuaweiPacket.MissingTagException { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21})); + String expectedOutput = "Hello world!"; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertEquals(expectedOutput, huaweiTLV.getString(tag)); + } + + @Test + public void testGetObject() throws HuaweiPacket.MissingTagException { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01, 0x00})); + HuaweiTLV expectedOutput = new HuaweiTLV().put(0x01); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + // assertEquals currently tests if the objects are the same, thus this would fail + // Assert.assertEquals(expectedOutput, huaweiTLV.getObject(tag)); + + HuaweiTLV result = huaweiTLV.getObject(tag); + + Assert.assertEquals(expectedOutput.valueMap, result.valueMap); + } + + @Test + public void testContains() { + int existingTag = 0x01; + int nonExistingTag = 0x02; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {})); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertTrue(huaweiTLV.contains(existingTag)); + Assert.assertFalse(huaweiTLV.contains(nonExistingTag)); + } + + @Test + public void testRemoveExisting() { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x13, 0x37})); + input.add(new HuaweiTLV.TLV((byte) 0x02, new byte[] {})); + byte[] expectedOutput = {0x13, 0x37}; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x02, new byte[] {})); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertArrayEquals(expectedOutput, huaweiTLV.remove(tag)); + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } + + @Test + public void testRemoveNonExisting() { + int tag = 0x02; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x13, 0x37})); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertNull(huaweiTLV.remove(tag)); + Assert.assertEquals(input, huaweiTLV.valueMap); + } + + @Test + public void testRemoveDouble() { + int tag = 0x01; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xCA})); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xFE})); + byte[] expectedOutput1 = {(byte) 0xFE}; + byte[] expectedOutput2 = {(byte) 0xCA}; + ArrayList expectedValueMap1 = new ArrayList<>(); + expectedValueMap1.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xCA})); + ArrayList expectedValueMap2 = new ArrayList<>(); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertArrayEquals(expectedOutput1, huaweiTLV.remove(tag)); + Assert.assertEquals(expectedValueMap1, huaweiTLV.valueMap); + Assert.assertArrayEquals(expectedOutput2, huaweiTLV.remove(tag)); + Assert.assertEquals(expectedValueMap2, huaweiTLV.valueMap); + } + + @Test + public void testToStringEmpty() { + ArrayList input = new ArrayList<>(); + String expectedOutput = "Empty"; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertEquals(expectedOutput, huaweiTLV.toString()); + } + + @Test + public void testToString() { + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {0x01, 0x02})); + input.add(new HuaweiTLV.TLV((byte) 0x02, new byte[] {0x03, 0x04})); + String expectedOutput = "{tag: 1 - Value: 0102} - {tag: 2 - Value: 0304}"; + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + Assert.assertEquals(expectedOutput, huaweiTLV.toString()); + } + + /** + * Following test also depends on the HuaweiCrypto class functioning correctly + */ + @Test + public void testEncrypt() throws HuaweiCrypto.CryptoException { + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xCA, (byte) 0xFE})); + + byte[] expectedCiphertext = {(byte) 0x0E, (byte) 0xA0, (byte) 0x01, (byte) 0xBB, (byte) 0x1E, (byte) 0xDA, (byte) 0xCB, (byte) 0x09, (byte) 0x83, (byte) 0x20, (byte) 0x40, (byte) 0x7D, (byte) 0x97, (byte) 0x1B, (byte) 0xF6, (byte) 0xD0}; + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x7C, new byte[] {0x01})); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x7D, secretsProvider.getIv())); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x7E, expectedCiphertext)); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + HuaweiTLV encryptedTlv = huaweiTLV.encrypt(secretsProvider); + + Assert.assertEquals(input, huaweiTLV.valueMap); + Assert.assertEquals(expectedValueMap, encryptedTlv.valueMap); + } + + /** + * Following test also depends on the HuaweiCrypto class functioning correctly + */ + @Test + public void testDecrypt() throws HuaweiCrypto.CryptoException, HuaweiPacket.MissingTagException { + byte[] ciphertext = {(byte) 0x0E, (byte) 0xA0, (byte) 0x01, (byte) 0xBB, (byte) 0x1E, (byte) 0xDA, (byte) 0xCB, (byte) 0x09, (byte) 0x83, (byte) 0x20, (byte) 0x40, (byte) 0x7D, (byte) 0x97, (byte) 0x1B, (byte) 0xF6, (byte) 0xD0}; + ArrayList input = new ArrayList<>(); + input.add(new HuaweiTLV.TLV((byte) 0x7C, new byte[] {0x01})); + input.add(new HuaweiTLV.TLV((byte) 0x7D, secretsProvider.getIv())); + input.add(new HuaweiTLV.TLV((byte) 0x7E, ciphertext)); + + ArrayList expectedValueMap = new ArrayList<>(); + expectedValueMap.add(new HuaweiTLV.TLV((byte) 0x01, new byte[] {(byte) 0xCA, (byte) 0xFE})); + + HuaweiTLV huaweiTLV = new HuaweiTLV(); + huaweiTLV.valueMap = input; + + huaweiTLV.decrypt(secretsProvider); + + Assert.assertEquals(expectedValueMap, huaweiTLV.valueMap); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestVarInt.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestVarInt.java new file mode 100644 index 000000000..fde67f548 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/TestVarInt.java @@ -0,0 +1,72 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei; + +import org.junit.Assert; +import org.junit.Test; + +public class TestVarInt { + + private void testValue(int intValue, byte[] bytesValue, int size) { + Assert.assertEquals(size, VarInt.getVarIntSize(intValue)); + Assert.assertEquals(intValue, VarInt.getVarIntValue(bytesValue, 0)); + Assert.assertArrayEquals(bytesValue, VarInt.putVarIntValue(intValue)); + + VarInt varInt = new VarInt(bytesValue, 0); + Assert.assertEquals(size, varInt.size); + Assert.assertEquals(intValue, varInt.dValue); + Assert.assertArrayEquals(bytesValue, varInt.eValue); + } + + @Test + public void testZero() { + testValue(0, new byte[]{0}, 1); + } + + @Test + public void testSingleValue() { + testValue(17, new byte[]{17}, 1); + } + + @Test + public void test0x80() { + // This is 1 << 8, the first 'overflowing' value + testValue(0x80, new byte[]{(byte) 0x81, 0x00}, 2); + } + + @Test + public void testDoubleValue() { + testValue(460, new byte[]{(byte) 0x83, 0x4C}, 2); + } + + @Test + public void testOffset() { + int intValue = 460; + byte[] bytesValue = {(byte) 0x83, 0x4C}; + byte[] bytesTest = {0x00, (byte) 0x83, 0x4C}; + int size = 2; + + Assert.assertEquals(size, VarInt.getVarIntSize(intValue)); + Assert.assertEquals(intValue, VarInt.getVarIntValue(bytesTest, 1)); + Assert.assertArrayEquals(bytesValue, VarInt.putVarIntValue(intValue)); + + VarInt varInt = new VarInt(bytesTest, 1); + Assert.assertEquals(size, varInt.size); + Assert.assertEquals(intValue, varInt.dValue); + Assert.assertArrayEquals(bytesValue, varInt.eValue); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestAlarms.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestAlarms.java new file mode 100644 index 000000000..0712dd247 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestAlarms.java @@ -0,0 +1,167 @@ +/* Copyright (C) 2022-2023 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +public class TestAlarms { + + HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testEventAlarmsRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + Field alarmField = Alarms.EventAlarmsRequest.class.getDeclaredField("alarms"); + alarmField.setAccessible(true); + + HuaweiTLV expectedAlarmsTlv = new HuaweiTLV(); + + Alarms.EventAlarmsRequest request = new Alarms.EventAlarmsRequest(paramsProvider); + + expectedAlarmsTlv.put(0x82, new HuaweiTLV() + .put(0x03, (byte) 1) + .put(0x04, true) + .put(0x05, (short) 0x1337) + .put(0x06, (byte) 0) + .put(0x07, "Alarm1") + ); + + request.addEventAlarm( + new Alarms.EventAlarm( + (byte) 1, + true, + (byte) 0x13, + (byte) 0x37, + (byte) 0, + "Alarm1" + ) + ); + + Assert.assertEquals(0x08, request.serviceId); + Assert.assertEquals(0x01, request.commandId); + // TODO: check count in request + Assert.assertEquals(expectedAlarmsTlv, alarmField.get(request)); + + // A serialize will change the tlv, so we cannot test it here + + expectedAlarmsTlv.put(0x82,new HuaweiTLV() + .put(0x03, (byte) 2) + .put(0x04, false) + .put(0x05, (short) 0xCAFE) + .put(0x06, (byte) 1) + .put(0x07, "Alarm2") + ); + + request.addEventAlarm( + new Alarms.EventAlarm( + (byte) 2, + false, + (byte) 0xCA, + (byte) 0xFE, + (byte) 1, + "Alarm2" + ) + ); + + Assert.assertEquals(expectedAlarmsTlv, alarmField.get(request)); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, expectedAlarmsTlv); + + // Different order for better assertion messages in case of failure + List listOut = request.serialize(); + Assert.assertEquals(1, listOut.size()); + Assert.assertEquals(expectedAlarmsTlv, alarmField.get(request)); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + } + + @Test + public void testSmartAlarmRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + byte[] expectedOutput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x08, (byte) 0x02, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x20, (byte) 0xcd, (byte) 0x7f, (byte) 0x80, (byte) 0x67, (byte) 0x02, (byte) 0x8d, (byte) 0x46, (byte) 0xfb, (byte) 0xc1, (byte) 0x0b, (byte) 0xed, (byte) 0x6c, (byte) 0x46, (byte) 0xb7, (byte) 0x59, (byte) 0xba, (byte) 0x08, (byte) 0xfd, (byte) 0xde, (byte) 0x3b, (byte) 0xee, (byte) 0x54, (byte) 0xbd, (byte) 0x4f, (byte) 0x27, (byte) 0xf6, (byte) 0x52, (byte) 0x9a, (byte) 0xae, (byte) 0xbf, (byte) 0x55, (byte) 0xd9, (byte) 0xe0, (byte) 0xa6}; + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x81, new HuaweiTLV() + .put(0x82, new HuaweiTLV() + .put(0x03, (byte) 0x01) + .put(0x04, true) + .put(0x05, (short) 0x1337) + .put(0x06, (byte) 1) + .put(0x07, (byte) 2) + ) + ); + + Alarms.SmartAlarmRequest request = new Alarms.SmartAlarmRequest( + paramsProvider, + new Alarms.SmartAlarm( + true, + (byte) 0x13, + (byte) 0x37, + (byte) 1, + (byte) 2 + ) + ); + + Assert.assertEquals(0x08, request.serviceId); + Assert.assertEquals(0x02, request.commandId); + Assert.assertTrue(request.complete); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expectedOutput, out.get(0)); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestCalls.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestCalls.java new file mode 100644 index 000000000..366a19d05 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestCalls.java @@ -0,0 +1,68 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestCalls { + + @Test + public void testAnswerCallResponseAccept() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x99, (byte) 0x6B}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x01, (byte) 0x02); + + HuaweiPacket packet = new HuaweiPacket(null).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x04, packet.serviceId); + Assert.assertEquals(0x01, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof Calls.AnswerCallResponse); + Assert.assertEquals(Calls.AnswerCallResponse.Action.CALL_ACCEPT, ((Calls.AnswerCallResponse) packet).action); + } + + @Test + public void testAnswerCallResponseReject() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xA9, (byte) 0x08}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x01, (byte) 0x01); + + HuaweiPacket packet = new HuaweiPacket(null).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x04, packet.serviceId); + Assert.assertEquals(0x01, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof Calls.AnswerCallResponse); + Assert.assertEquals(Calls.AnswerCallResponse.Action.CALL_REJECT, ((Calls.AnswerCallResponse) packet).action); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestDeviceConfig.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestDeviceConfig.java new file mode 100644 index 000000000..d8d48c180 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestDeviceConfig.java @@ -0,0 +1,505 @@ +/* Copyright (C) 2022 Gaignon Damien, Martin.JM + Copyright (C) 2022-2023 MartinJM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinatorSupplier; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.TimeZone; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +public class TestDeviceConfig { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testLinkParamsRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01) + .put(0x02) + .put(0x03) + .put(0x04); + + byte[] serialized = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x0b, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0xf1, (byte) 0x3b}; + DeviceConfig.LinkParams.Request request = new DeviceConfig.LinkParams.Request( + secretsProvider, HuaweiCoordinatorSupplier.HuaweiDeviceType.BLE + ); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x01, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } + + @Test + public void testLinkParamsResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawLinkParams = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x1b, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x02, (byte) 0x00, (byte) 0x14, (byte) 0x05, (byte) 0x12, (byte) 0x00, (byte) 0x01, (byte) 0x6a, (byte) 0xa2, (byte) 0x96, (byte) 0xe3, (byte) 0x76, (byte) 0x41, (byte) 0xb1, (byte) 0x0c, (byte) 0xf8, (byte) 0xaa, (byte) 0xf7, (byte) 0x47, (byte) 0x05, (byte) 0x5d, (byte) 0x0a, (byte) 0xa3, (byte) 0xe8, (byte) 0x9f}; + + byte[] expectedServerNonceWithAuth = new byte[] {(byte) 0x00, (byte) 0x01, (byte) 0x6A, (byte) 0xA2, (byte) 0x96, (byte) 0xE3, (byte) 0x76, (byte) 0x41, (byte) 0xB1, (byte) 0x0C, (byte) 0xF8, (byte) 0xAA, (byte) 0xF7, (byte) 0x47, (byte) 0x05, (byte) 0x5D, (byte) 0x0A, (byte) 0xA3}; + byte[] expectedServerNonce = new byte[expectedServerNonceWithAuth.length - 2]; + System.arraycopy(expectedServerNonceWithAuth, 2, expectedServerNonce, 0, expectedServerNonceWithAuth.length - 2); + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x03, new byte[] {0x00, 0x14}) + .put(0x05, expectedServerNonceWithAuth); + + HuaweiPacket packetLinkParams = new HuaweiPacket(secretsProvider).parse(rawLinkParams); + packetLinkParams.parseTlv(); + + Assert.assertEquals(0x01, packetLinkParams.serviceId); + Assert.assertEquals(0x01, packetLinkParams.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packetLinkParams)); + Assert.assertTrue(packetLinkParams.complete); + Assert.assertTrue(packetLinkParams instanceof DeviceConfig.LinkParams.Response); + Assert.assertEquals(0x01, ((DeviceConfig.LinkParams.Response) packetLinkParams).authVersion); + Assert.assertArrayEquals(expectedServerNonce, ((DeviceConfig.LinkParams.Response) packetLinkParams).serverNonce); + } + + @Test(expected=HuaweiPacket.ParseException.class) + public void testLinkParamsResponseException() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawLinkParams = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x12, (byte) 0x00, (byte) 0x01, (byte) 0x6a, (byte) 0xa2, (byte) 0x96, (byte) 0xe3, (byte) 0x76, (byte) 0x41, (byte) 0xb1, (byte) 0x0c, (byte) 0xf8, (byte) 0xaa, (byte) 0xf7, (byte) 0x47, (byte) 0x05, (byte) 0x5d, (byte) 0x0a, (byte) 0xa3, (byte) 0xdd, (byte) 0x41}; + + HuaweiPacket packetLinkParams = new HuaweiPacket(secretsProvider).parse(rawLinkParams); + packetLinkParams.parseTlv(); + } + + @Test + public void testSupportedServicesRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + byte[] allSupportedServices = new byte[] {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e, (byte) 0x0f, (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1a, (byte) 0x1b, (byte) 0x1c, (byte) 0x1d, (byte) 0x1e, (byte) 0x1f, (byte) 0x20, (byte) 0x21, (byte) 0x22, (byte) 0x23, (byte) 0x24, (byte) 0x25, (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29, (byte) 0x2a, (byte) 0x2b, (byte) 0x2c, (byte) 0x2d, (byte) 0x2e, (byte) 0x2f, (byte) 0x30, (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34, (byte) 0x35}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, allSupportedServices); + + byte[] serialized = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x5a, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x40, (byte) 0x14, (byte) 0xb1, (byte) 0x75, (byte) 0x7d, (byte) 0xc0, (byte) 0xa5, (byte) 0x32, (byte) 0xeb, (byte) 0xc1, (byte) 0x20, (byte) 0x7b, (byte) 0xb8, (byte) 0x59, (byte) 0xdb, (byte) 0xdb, (byte) 0xfe, (byte) 0x5b, (byte) 0x01, (byte) 0x0a, (byte) 0x7d, (byte) 0xb7, (byte) 0x76, (byte) 0xfc, (byte) 0xcc, (byte) 0x5f, (byte) 0x22, (byte) 0xff, (byte) 0x13, (byte) 0xcb, (byte) 0xbb, (byte) 0x4f, (byte) 0xe2, (byte) 0xcd, (byte) 0x6e, (byte) 0x4b, (byte) 0xd7, (byte) 0x7c, (byte) 0x05, (byte) 0x24, (byte) 0x85, (byte) 0x65, (byte) 0x5f, (byte) 0x95, (byte) 0x32, (byte) 0xb4, (byte) 0x5e, (byte) 0x16, (byte) 0xef, (byte) 0xad, (byte) 0x62, (byte) 0x38, (byte) 0xd5, (byte) 0x88, (byte) 0x63, (byte) 0xa4, (byte) 0xb0, (byte) 0x29, (byte) 0xbb, (byte) 0x90, (byte) 0x66, (byte) 0x8c, (byte) 0x3f, (byte) 0x58, (byte) 0x69, (byte) 0x40, (byte) 0x22}; + DeviceConfig.SupportedServices.Request request = new DeviceConfig.SupportedServices.Request( + secretsProvider, + allSupportedServices + ); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x02, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } + + @Test + public void testSupportedServicesResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawSupportedServices = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x5a, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x40, (byte) 0xC8, (byte) 0x9F, (byte) 0x1E, (byte) 0x2F, (byte) 0xE8, (byte) 0x31, (byte) 0xC8, (byte) 0x1E, (byte) 0x92, (byte) 0xB0, (byte) 0xE8, (byte) 0x9E, (byte) 0xC7, (byte) 0x2E, (byte) 0x76, (byte) 0xD7, (byte) 0x6C, (byte) 0x64, (byte) 0x22, (byte) 0x5A, (byte) 0x6C, (byte) 0xF9, (byte) 0xAF, (byte) 0xFB, (byte) 0x8E, (byte) 0x98, (byte) 0x74, (byte) 0xB6, (byte) 0xF9, (byte) 0x84, (byte) 0x3C, (byte) 0x1E, (byte) 0x3D, (byte) 0xCB, (byte) 0x7C, (byte) 0x23, (byte) 0x4F, (byte) 0x7B, (byte) 0x34, (byte) 0x0C, (byte) 0x49, (byte) 0xBD, (byte) 0x80, (byte) 0x94, (byte) 0x67, (byte) 0x1B, (byte) 0x5C, (byte) 0x64, (byte) 0x6B, (byte) 0xA4, (byte) 0xB9, (byte) 0xEC, (byte) 0xA7, (byte) 0x97, (byte) 0x95, (byte) 0x6F, (byte) 0x44, (byte) 0x13, (byte) 0x66, (byte) 0x7C, (byte) 0xF5, (byte) 0x9F, (byte) 0x05, (byte) 0x72, (byte) 0xc9, (byte) 0xe9}; + byte[] expectedSupportedServices = new byte[] {(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x02, expectedSupportedServices); + + HuaweiPacket packetSupportedServices = new HuaweiPacket(secretsProvider).parse(rawSupportedServices); + packetSupportedServices.parseTlv(); + + Assert.assertEquals(0x01, packetSupportedServices.serviceId); + Assert.assertEquals(0x02, packetSupportedServices.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packetSupportedServices)); + Assert.assertTrue(packetSupportedServices.complete); + Assert.assertTrue(packetSupportedServices instanceof DeviceConfig.SupportedServices.Response); + Assert.assertArrayEquals(expectedSupportedServices, ((DeviceConfig.SupportedServices.Response) packetSupportedServices).supportedServices); + } + + @Test(expected=HuaweiPacket.ParseException.class) + public void testSupportedServicesResponseException() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawSupportedServices = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x5A, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x40, (byte) 0x28, (byte) 0xB0, (byte) 0x4D, (byte) 0x00, (byte) 0xCF, (byte) 0xE1, (byte) 0xD1, (byte) 0x7A, (byte) 0x5D, (byte) 0xE7, (byte) 0x61, (byte) 0x0E, (byte) 0xE1, (byte) 0xA5, (byte) 0xE8, (byte) 0xF9, (byte) 0x6D, (byte) 0x2D, (byte) 0x32, (byte) 0xB3, (byte) 0xC3, (byte) 0x7C, (byte) 0x07, (byte) 0xBC, (byte) 0x11, (byte) 0x03, (byte) 0x8A, (byte) 0x66, (byte) 0x8C, (byte) 0x47, (byte) 0x94, (byte) 0x86, (byte) 0x8C, (byte) 0x0D, (byte) 0xC6, (byte) 0xBC, (byte) 0xDF, (byte) 0xB3, (byte) 0x00, (byte) 0xFB, (byte) 0x68, (byte) 0x11, (byte) 0xC1, (byte) 0xB3, (byte) 0x66, (byte) 0x6D, (byte) 0x85, (byte) 0x6F, (byte) 0xF0, (byte) 0xA9, (byte) 0xD0, (byte) 0x49, (byte) 0xDF, (byte) 0xF5, (byte) 0x82, (byte) 0x01, (byte) 0x9F, (byte) 0xE4, (byte) 0x60, (byte) 0x36, (byte) 0x81, (byte) 0xAA, (byte) 0x31, (byte) 0xA1, (byte) 0x39, (byte) 0xD6}; + + HuaweiPacket packetSupportedServices = new HuaweiPacket(secretsProvider).parse(rawSupportedServices); + packetSupportedServices.parseTlv(); + } + + @Test + public void testSupportedCommandsRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + byte service1 = (byte) 0x01; + byte[] commands1 = new byte[]{(byte) 0x04, (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0D, (byte) 0x0E, (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x1B, (byte) 0x1A, (byte) 0x1D, (byte) 0x21, (byte) 0x22, (byte) 0x23, (byte) 0x24, (byte) 0x29, (byte) 0x2A, (byte) 0x2B, (byte) 0x32, (byte) 0x2E, (byte) 0x31, (byte) 0x30, (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x2F}; + byte service2 = (byte) 0x02; + byte[] commands2 = new byte[]{(byte) 0x01, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08}; + byte service3 = (byte) 0x04; + byte[] commands3 = new byte[]{(byte) 0x01}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x81, new HuaweiTLV() + .put(0x02, service1) + .put(0x03, commands1) + .put(0x02, service2) + .put(0x03, commands2) + .put(0x02, service3) + .put(0x03, commands3) + ); + + byte[] expectedSerialized = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x5A, (byte) 0x00, (byte) 0x01, (byte) 0x03, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x40, (byte) 0x81, (byte) 0xED, (byte) 0x07, (byte) 0xF6, (byte) 0x51, (byte) 0xA3, (byte) 0x19, (byte) 0xBD, (byte) 0xCE, (byte) 0x35, (byte) 0x13, (byte) 0x23, (byte) 0x1E, (byte) 0xFC, (byte) 0x1A, (byte) 0x51, (byte) 0x92, (byte) 0xBB, (byte) 0x43, (byte) 0xC5, (byte) 0xF5, (byte) 0xD9, (byte) 0x4E, (byte) 0xCC, (byte) 0x2F, (byte) 0xE0, (byte) 0xDB, (byte) 0xB1, (byte) 0x5E, (byte) 0x78, (byte) 0x66, (byte) 0x69, (byte) 0x61, (byte) 0x85, (byte) 0x46, (byte) 0xB2, (byte) 0x50, (byte) 0xEC, (byte) 0xB5, (byte) 0x3F, (byte) 0x74, (byte) 0x68, (byte) 0x47, (byte) 0x03, (byte) 0x87, (byte) 0xC1, (byte) 0xB3, (byte) 0x53, (byte) 0x7B, (byte) 0x53, (byte) 0xDB, (byte) 0xE8, (byte) 0x5E, (byte) 0x82, (byte) 0x56, (byte) 0xFD, (byte) 0x16, (byte) 0x66, (byte) 0x03, (byte) 0xB2, (byte) 0x56, (byte) 0xA3, (byte) 0x14, (byte) 0x70, (byte) 0x38, (byte) 0x3E}; + DeviceConfig.SupportedCommands.Request request = new DeviceConfig.SupportedCommands.Request( + secretsProvider + ); + request.addCommandsForService(service1, commands1); + request.addCommandsForService(service2, commands2); + request.addCommandsForService(service3, commands3); + + List out = request.serialize(); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x03, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expectedSerialized, out.get(0)); + } + + @Test + public void testSupportedCommandsResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawSupportedCommands = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x5A, (byte) 0x00, (byte) 0x01, (byte) 0x03, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x40, (byte) 0x8B, (byte) 0x86, (byte) 0x29, (byte) 0xE3, (byte) 0x90, (byte) 0x25, (byte) 0x4E, (byte) 0x14, (byte) 0xE1, (byte) 0xDD, (byte) 0x96, (byte) 0x63, (byte) 0x66, (byte) 0xB8, (byte) 0x1E, (byte) 0x4A, (byte) 0xC1, (byte) 0xA7, (byte) 0x49, (byte) 0xB0, (byte) 0x9F, (byte) 0x21, (byte) 0x7C, (byte) 0xE8, (byte) 0x2C, (byte) 0x72, (byte) 0x93, (byte) 0x9F, (byte) 0xAC, (byte) 0x37, (byte) 0x3B, (byte) 0x4D, (byte) 0x1A, (byte) 0xCB, (byte) 0xC2, (byte) 0xFF, (byte) 0x64, (byte) 0xE5, (byte) 0xF0, (byte) 0x3E, (byte) 0x5B, (byte) 0xFF, (byte) 0xB1, (byte) 0x9C, (byte) 0x59, (byte) 0xB2, (byte) 0xF1, (byte) 0xD6, (byte) 0x4B, (byte) 0x2B, (byte) 0x99, (byte) 0xFB, (byte) 0xEA, (byte) 0x29, (byte) 0x66, (byte) 0xD3, (byte) 0x90, (byte) 0x0B, (byte) 0xC9, (byte) 0xF0, (byte) 0xB4, (byte) 0x9B, (byte) 0x3B, (byte) 0x3E, (byte) 0x50, (byte) 0xFA}; + + List expectedSupportedCommandsList = new ArrayList<>(); + DeviceConfig.SupportedCommands.Response.CommandsList commandsList = new DeviceConfig.SupportedCommands.Response.CommandsList(); + commandsList.service = 1; + commandsList.commands = new byte[] {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x0D, (byte) 0x0F, (byte) 0x10, (byte) 0x11}; + expectedSupportedCommandsList.add(commandsList); + commandsList.service = 2; + commandsList.commands = new byte[] {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x06}; + expectedSupportedCommandsList.add(commandsList); + commandsList.service = 4; + commandsList.commands = new byte[] {(byte) 0x01}; + expectedSupportedCommandsList.add(commandsList); + + byte[] expectedSupportedCommands = new byte[] {(byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0x1E, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x02, (byte) 0x04, (byte) 0x06, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x04, (byte) 0x04, (byte) 0x01, (byte) 0x01}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x81, expectedSupportedCommands); + + HuaweiPacket packetSupportedCommands = new HuaweiPacket(secretsProvider).parse(rawSupportedCommands); + packetSupportedCommands.parseTlv(); + + Assert.assertEquals(0x01, packetSupportedCommands.serviceId); + Assert.assertEquals(0x03, packetSupportedCommands.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packetSupportedCommands)); + Assert.assertTrue(packetSupportedCommands.complete); + Assert.assertTrue(packetSupportedCommands instanceof DeviceConfig.SupportedCommands.Response); + Assert.assertEquals(expectedSupportedCommandsList.size(), ((DeviceConfig.SupportedCommands.Response) packetSupportedCommands).commandsLists.size()); + } + + @Test(expected=HuaweiPacket.ParseException.class) + public void testSupportedCommandsResponseException02() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawSupportedCommands = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x4A, (byte) 0x00, (byte) 0x01, (byte) 0x03, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x30, (byte) 0x57, (byte) 0xC7, (byte) 0xF7, (byte) 0x47, (byte) 0x0E, (byte) 0xA9, (byte) 0xA2, (byte) 0x9E, (byte) 0xF3, (byte) 0xB0, (byte) 0xBD, (byte) 0x02, (byte) 0xE0, (byte) 0x79, (byte) 0x3C, (byte) 0x12, (byte) 0xE6, (byte) 0x58, (byte) 0xDA, (byte) 0xF7, (byte) 0x0B, (byte) 0xC3, (byte) 0x93, (byte) 0x8D, (byte) 0x37, (byte) 0x2E, (byte) 0xA9, (byte) 0xB8, (byte) 0xF8, (byte) 0xF7, (byte) 0x97, (byte) 0xF3, (byte) 0x22, (byte) 0x08, (byte) 0xDF, (byte) 0xAD, (byte) 0x2B, (byte) 0x62, (byte) 0x33, (byte) 0x11, (byte) 0x93, (byte) 0x66, (byte) 0xD1, (byte) 0xAE, (byte) 0xF3, (byte) 0x02, (byte) 0x18, (byte) 0x49, (byte) 0xD0, (byte) 0x40}; + + HuaweiPacket packetSupportedCommands = new HuaweiPacket(secretsProvider).parse(rawSupportedCommands); + packetSupportedCommands.parseTlv(); + } + + @Test(expected=HuaweiPacket.ParseException.class) + public void testSupportedCommandsResponseException04() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawSupportedCommands = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x2A, (byte) 0x00, (byte) 0x01, (byte) 0x03, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x10, (byte) 0x25, (byte) 0x26, (byte) 0xE0, (byte) 0x79, (byte) 0x1D, (byte) 0x19, (byte) 0x82, (byte) 0xDB, (byte) 0x0A, (byte) 0x3A, (byte) 0x21, (byte) 0x6E, (byte) 0x70, (byte) 0x52, (byte) 0xAB, (byte) 0xF3, (byte) 0x14, (byte) 0xB4}; + + HuaweiPacket packetSupportedCommands = new HuaweiPacket(secretsProvider).parse(rawSupportedCommands); + packetSupportedCommands.parseTlv(); + } + + @Test + public void testDateFormatRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + byte dateformat = (byte) 0x02; + byte timeFormat = (byte) 0x02; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x81, new HuaweiTLV() + .put(0x02, dateformat) + .put(0x03, timeFormat) + ); + + byte[] serialized = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x01, (byte) 0x04, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x63, (byte) 0xe0, (byte) 0xf1, (byte) 0xbf, (byte) 0x40, (byte) 0xab, (byte) 0x09, (byte) 0x63, (byte) 0x51, (byte) 0x7c, (byte) 0xa7, (byte) 0x8c, (byte) 0x2e, (byte) 0xd9, (byte) 0x6a, (byte) 0x6c, (byte) 0xdc, (byte) 0xe9}; + DeviceConfig.DateFormat.Request request = new DeviceConfig.DateFormat.Request ( + secretsProvider, + dateformat, + timeFormat + ); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x04, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } + + @Test + public void testTimeRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + int timestamp = 1633987331; + short zoneOffset = (short) 512; + + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(timestamp * 1000L); + calendar.setTimeZone(TimeZone.getTimeZone("GMT+2")); + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, timestamp) + .put(0x02, zoneOffset); + + byte[] serialized = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x2A, (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x10, (byte) 0xED, (byte) 0x67, (byte) 0x61, (byte) 0x8A, (byte) 0x8E, (byte) 0x44, (byte) 0x67, (byte) 0xB1, (byte) 0x2A, (byte) 0xB4, (byte) 0xFA, (byte) 0x86, (byte) 0x76, (byte) 0x17, (byte) 0x8C, (byte) 0x61, (byte) 0xFC, (byte) 0x99}; + DeviceConfig.TimeRequest request = new DeviceConfig.TimeRequest(secretsProvider, calendar); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x05, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } + + @Test + public void testProductInformationRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV(); + byte[] expectedTags = {0x01, 0x02, 0x07, 0x09, 0x0A, 0x11, 0x12, 0x16, 0x1A, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23}; + for (byte tag : expectedTags) { + expectedTlv.put(tag); + } + + // Outdated + //byte[] serialized = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x3A, (byte) 0x00, (byte) 0x01, (byte) 0x07, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x20, (byte) 0x10, (byte) 0x9B, (byte) 0x27, (byte) 0x5D, (byte) 0xB1, (byte) 0x3C, (byte) 0xFD, (byte) 0x40, (byte) 0x4B, (byte) 0xA8, (byte) 0xAC, (byte) 0xAF, (byte) 0x8A, (byte) 0xB6, (byte) 0xA5, (byte) 0x3D, (byte) 0x40, (byte) 0x30, (byte) 0x2C, (byte) 0x79, (byte) 0x98, (byte) 0x6D, (byte) 0xEC, (byte) 0xD1, (byte) 0x39, (byte) 0xE6, (byte) 0xFE, (byte) 0x5C, (byte) 0xE8, (byte) 0xB2, (byte) 0xF3, (byte) 0x9E, (byte) 0x3E, (byte) 0x1B}; + DeviceConfig.ProductInfo.Request request = new DeviceConfig.ProductInfo.Request ( + secretsProvider, HuaweiCoordinatorSupplier.HuaweiDeviceType.BLE + ); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x07, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + // Assert.assertArrayEquals(serialized, out.get(0)); + } + + @Test + public void testProductInformationResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawProductInformation = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x4A, (byte) 0x00, (byte) 0x01, (byte) 0x07, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x30, (byte) 0x50, (byte) 0x75, (byte) 0xA5, (byte) 0x4F, (byte) 0x26, (byte) 0xF7, (byte) 0x74, (byte) 0x0B, (byte) 0xB2, (byte) 0xD8, (byte) 0x01, (byte) 0xBA, (byte) 0xDC, (byte) 0x7E, (byte) 0x40, (byte) 0x36, (byte) 0xD5, (byte) 0x6D, (byte) 0x4B, (byte) 0x7B, (byte) 0x8F, (byte) 0xC6, (byte) 0xFB, (byte) 0x48, (byte) 0xFC, (byte) 0x89, (byte) 0x54, (byte) 0xF8, (byte) 0xBB, (byte) 0xC0, (byte) 0x48, (byte) 0x9E, (byte) 0x34, (byte) 0x0E, (byte) 0xB1, (byte) 0x24, (byte) 0xD8, (byte) 0x89, (byte) 0x02, (byte) 0x7E, (byte) 0x6C, (byte) 0x3E, (byte) 0x81, (byte) 0x7D, (byte) 0x38, (byte) 0x0F, (byte) 0xD9, (byte) 0x2A, (byte) 0x98, (byte) 0xE3}; + String softwareVersion = "1.0.10.78"; + String productModel = "Crius"; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x03, new byte[] {(byte) 0x4E, (byte) 0x41}) + .put(0x07, new byte[] {(byte) 0x31, (byte) 0x2E, (byte) 0x30, (byte) 0x2E, (byte) 0x31, (byte) 0x30, (byte) 0x2E, (byte) 0x37, (byte) 0x38}) + .put(0x0A, new byte[] {(byte) 0x43, (byte) 0x72, (byte) 0x69, (byte) 0x75, (byte) 0x73, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}); + + HuaweiPacket packetProductInformation = new HuaweiPacket(secretsProvider).parse(rawProductInformation); + packetProductInformation.parseTlv(); + + Assert.assertEquals(0x01, packetProductInformation.serviceId); + Assert.assertEquals(0x07, packetProductInformation.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packetProductInformation)); + Assert.assertTrue(packetProductInformation.complete); + Assert.assertTrue(packetProductInformation instanceof DeviceConfig.ProductInfo.Response); + Assert.assertTrue(softwareVersion.equals(((DeviceConfig.ProductInfo.Response) packetProductInformation).softwareVersion)); + System.out.println(((DeviceConfig.ProductInfo.Response) packetProductInformation).productModel); + System.out.println(((DeviceConfig.ProductInfo.Response) packetProductInformation).productModel.length()); + Assert.assertTrue(productModel.equals(((DeviceConfig.ProductInfo.Response) packetProductInformation).productModel)); + } + + @Test(expected=HuaweiPacket.ParseException.class) + public void testProductInformationResponseException07() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawProductInformation = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x3A, (byte) 0x00, (byte) 0x01, (byte) 0x07, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x20, (byte) 0xFE, (byte) 0x24, (byte) 0x4C, (byte) 0x68, (byte) 0xFC, (byte) 0x1D, (byte) 0xAD, (byte) 0x64, (byte) 0x77, (byte) 0xC9, (byte) 0xE9, (byte) 0x26, (byte) 0x8D, (byte) 0x3C, (byte) 0x3C, (byte) 0x8C, (byte) 0xB6, (byte) 0xA6, (byte) 0xF1, (byte) 0xBF, (byte) 0xAC, (byte) 0xB6, (byte) 0x7A, (byte) 0x75, (byte) 0xA3, (byte) 0xA9, (byte) 0x07, (byte) 0x5F, (byte) 0x39, (byte) 0x0F, (byte) 0x28, (byte) 0x61, (byte) 0x50, (byte) 0x61}; + + HuaweiPacket packetProductInformation = new HuaweiPacket(secretsProvider).parse(rawProductInformation); + packetProductInformation.parseTlv(); + } + + @Test(expected=HuaweiPacket.ParseException.class) + public void testProductInformationResponseException0A() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawProductInformation = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x2A, (byte) 0x00, (byte) 0x01, (byte) 0x07, (byte) 0x7C, (byte) 0x01, (byte) 0x01, (byte) 0x7D, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7E, (byte) 0x10, (byte) 0xB6, (byte) 0x67, (byte) 0xA5, (byte) 0x6A, (byte) 0x46, (byte) 0x0F, (byte) 0x08, (byte) 0x1E, (byte) 0xAC, (byte) 0x1E, (byte) 0x6B, (byte) 0xF2, (byte) 0x11, (byte) 0x4A, (byte) 0x54, (byte) 0x20, (byte) 0xCF, (byte) 0xB6}; + + HuaweiPacket packetProductInformation = new HuaweiPacket(secretsProvider).parse(rawProductInformation); + packetProductInformation.parseTlv(); + } + + @Test + public void testBondRequest() throws NoSuchFieldException, IllegalAccessException { + byte[] clientSerial = new byte[] {(byte) 0x54, (byte) 0x56, (byte) 0x64, (byte) 0x54, (byte) 0x4D, (byte) 0x44}; + String mac = "FF:FF:FF:FF:FF:CC"; + HuaweiCrypto huaweiCrypto = new HuaweiCrypto(0x01); + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + try { + byte[] encryptionKey = huaweiCrypto.createSecretKey(mac); + byte[] iv = secretsProvider.getIv(); + byte[] key = huaweiCrypto.encryptBondingKey(secretsProvider.getEncryptMethod(), secretsProvider.getSecretKey(), encryptionKey, iv); + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01) + .put(0x03, (byte) 0x00) + .put(0x05, clientSerial) + .put(0x06, key) + .put(0x07, iv); + + byte[] serialized = new byte[]{(byte) 0x5A, (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x01, (byte) 0x0E, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x05, (byte) 0x06, (byte) 0x54, (byte) 0x56, (byte) 0x64, (byte) 0x54, (byte) 0x4D, (byte) 0x44, (byte) 0x06, (byte) 0x20, (byte) 0x88, (byte) 0x45, (byte) 0xAA, (byte) 0xB5, (byte) 0x9C, (byte) 0x84, (byte) 0x39, (byte) 0xAE, (byte) 0xD8, (byte) 0xE9, (byte) 0x71, (byte) 0x01, (byte) 0x5D, (byte) 0xC8, (byte) 0x34, (byte) 0x05, (byte) 0xC5, (byte) 0x9A, (byte) 0x6B, (byte) 0xDB, (byte) 0x62, (byte) 0x7D, (byte) 0xC8, (byte) 0xC3, (byte) 0xF4, (byte) 0xCC, (byte) 0x30, (byte) 0x74, (byte) 0x21, (byte) 0xD4, (byte) 0x45, (byte) 0x0E, (byte) 0x07, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x72, (byte) 0xFC}; + DeviceConfig.Bond.Request request = new DeviceConfig.Bond.Request( + secretsProvider, + clientSerial, + key, + iv + ); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x0E, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | BadPaddingException | InvalidKeyException | HuaweiPacket.CryptoException e) { + e.printStackTrace(); + Assert.fail(); + } + } + + @Test + public void testBondParamsRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + byte[] clientSerial = new byte[] {(byte) 0x54, (byte) 0x56, (byte) 0x64, (byte) 0x54, (byte) 0x4D, (byte) 0x44}; + byte[] mac = new byte[] {(byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x43, (byte) 0x43}; + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01) + .put(0x03, clientSerial) + .put(0x04, (byte) 0x02) + .put(0x05) + .put(0x07, mac) + .put(0x09); + + byte[] serialized = new byte[] {(byte) 0x5A, (byte) 0x00, (byte) 0x27, (byte) 0x00, (byte) 0x01, (byte) 0x0F, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x06, (byte) 0x54, (byte) 0x56, (byte) 0x64, (byte) 0x54, (byte) 0x4D, (byte) 0x44, (byte) 0x04, (byte) 0x01, (byte) 0x02, (byte) 0x05, (byte) 0x00, (byte) 0x07, (byte) 0x11, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x46, (byte) 0x46, (byte) 0x3A, (byte) 0x43, (byte) 0x43, (byte) 0x09, (byte) 0x00, (byte) 0xE5, (byte) 0xD8}; + DeviceConfig.BondParams.Request request = new DeviceConfig.BondParams.Request( + secretsProvider, + clientSerial, + mac + ); + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x0F, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } + + @Test + public void testAuthRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + byte[] challenge = new byte[] {(byte) 0x9D, (byte) 0xF6, (byte) 0x52, (byte) 0x69, (byte) 0x06, (byte) 0x7B, (byte) 0xEB, (byte) 0x46, (byte) 0x94, (byte) 0xAD, (byte) 0x35, (byte) 0xE2, (byte) 0x88, (byte) 0xC3, (byte) 0x84, (byte) 0x24, (byte) 0xA2, (byte) 0x55, (byte) 0xD8, (byte) 0x0F, (byte) 0xA7, (byte) 0x68, (byte) 0x21, (byte) 0x9B, (byte) 0xA1, (byte) 0xC3, (byte) 0xDC, (byte) 0x09, (byte) 0x24, (byte) 0x81, (byte) 0x51, (byte) 0x61}; + byte[] nonce = new byte[] {(byte) 0x00, (byte) 0x01, (byte) 0xBF, (byte) 0x1F, (byte) 0xEF, (byte) 0x9F, (byte) 0xF0, (byte) 0xFE, (byte) 0xEF, (byte) 0xEF, (byte) 0x9F, (byte) 0xEF, (byte) 0xF0, (byte) 0xEF, (byte) 0xF8, (byte) 0xFA, (byte) 0xEF, (byte) 0xF0}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, challenge) + .put(0x02, nonce); + + byte[] serialized = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x39, (byte) 0x00, (byte) 0x01, (byte) 0x13, (byte) 0x01, (byte) 0x20, (byte) 0x9d, (byte) 0xf6, (byte) 0x52, (byte) 0x69, (byte) 0x06, (byte) 0x7b, (byte) 0xeb, (byte) 0x46, (byte) 0x94, (byte) 0xad, (byte) 0x35, (byte) 0xe2, (byte) 0x88, (byte) 0xc3, (byte) 0x84, (byte) 0x24, (byte) 0xa2, (byte) 0x55, (byte) 0xd8, (byte) 0x0f, (byte) 0xa7, (byte) 0x68, (byte) 0x21, (byte) 0x9b, (byte) 0xa1, (byte) 0xc3, (byte) 0xdc, (byte) 0x09, (byte) 0x24, (byte) 0x81, (byte) 0x51, (byte) 0x61, (byte) 0x02, (byte) 0x12, (byte) 0x00, (byte) 0x01, (byte) 0xbf, (byte) 0x1f, (byte) 0xef, (byte) 0x9f, (byte) 0xf0, (byte) 0xfe, (byte) 0xef, (byte) 0xef, (byte) 0x9f, (byte) 0xef, (byte) 0xf0, (byte) 0xef, (byte) 0xf8, (byte) 0xfa, (byte) 0xef, (byte) 0xf0, (byte) 0xdc, (byte) 0x88}; + DeviceConfig.Auth.Request request = new DeviceConfig.Auth.Request( + secretsProvider, + challenge, + nonce + ); + + Assert.assertEquals(0x01, request.serviceId); + Assert.assertEquals(0x13, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestDisconnectNotification.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestDisconnectNotification.java new file mode 100644 index 000000000..9db856237 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestDisconnectNotification.java @@ -0,0 +1,104 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestDisconnectNotification { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testBluetoothDisconnectNotificationSetting() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlvEnable = new HuaweiTLV() + .put(0x01, true); + + byte[] serializedEnable = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x0b, (byte) 0x03, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xcd, (byte) 0x97, (byte) 0x7e, (byte) 0x01, (byte) 0x48, (byte) 0x34, (byte) 0x2a, (byte) 0x48, (byte) 0x58, (byte) 0x0d, (byte) 0x30, (byte) 0xc7, (byte) 0xbc, (byte) 0x2e, (byte) 0x40, (byte) 0xd4, (byte) 0x20, (byte) 0xaf}; + + DisconnectNotification.DisconnectNotificationSetting.Request requestEnable = new DisconnectNotification.DisconnectNotificationSetting.Request( + secretsProvider, + true + ); + + Assert.assertEquals(0x0b, requestEnable.serviceId); + Assert.assertEquals(0x03, requestEnable.commandId); + Assert.assertEquals(expectedTlvEnable, tlvField.get(requestEnable)); + Assert.assertTrue(requestEnable.complete); + List outEnable = requestEnable.serialize(); + Assert.assertEquals(1, outEnable.size()); + Assert.assertArrayEquals(serializedEnable, outEnable.get(0)); + + + HuaweiTLV expectedTlvDisable = new HuaweiTLV() + .put(0x01, false); + + byte[] serializedDisable = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x0b, (byte) 0x03, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x28, (byte) 0x00, (byte) 0x99, (byte) 0x6f, (byte) 0x2a, (byte) 0xcb, (byte) 0x62, (byte) 0x3a, (byte) 0xe6, (byte) 0x54, (byte) 0x28, (byte) 0x54, (byte) 0xf8, (byte) 0xab, (byte) 0x54, (byte) 0x83, (byte) 0x39, (byte) 0x9D}; + + DisconnectNotification.DisconnectNotificationSetting.Request requestDisable = new DisconnectNotification.DisconnectNotificationSetting.Request( + secretsProvider, + false + ); + + Assert.assertEquals(0x0b, requestDisable.serviceId); + Assert.assertEquals(0x03, requestDisable.commandId); + Assert.assertEquals(expectedTlvDisable, tlvField.get(requestDisable)); + Assert.assertTrue(requestDisable.complete); + List outDisable = requestDisable.serialize(); + Assert.assertEquals(1, outDisable.size()); + Assert.assertArrayEquals(serializedDisable, outDisable.get(0)); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestFindPhone.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestFindPhone.java new file mode 100644 index 000000000..7204f200f --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestFindPhone.java @@ -0,0 +1,120 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestFindPhone { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testStartFindPhone() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x0b, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xcc, (byte) 0xf1}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x01, true); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x0b, packet.serviceId); + Assert.assertEquals(0x01, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof FindPhone.Response); + Assert.assertTrue(((FindPhone.Response) packet).start); + } + + @Test + public void testStopFindPhone() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x0b, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xdc, (byte) 0xd0}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x01, false); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x0b, packet.serviceId); + Assert.assertEquals(0x01, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof FindPhone.Response); + Assert.assertFalse(((FindPhone.Response) packet).start); + } + + @Test + public void testFindPhoneMissingTag() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x0b, (byte) 0x01, (byte) 0xa1, (byte) 0x91}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV(); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x0b, packet.serviceId); + Assert.assertEquals(0x01, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof FindPhone.Response); + Assert.assertFalse(((FindPhone.Response) packet).start); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestFitnessData.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestFitnessData.java new file mode 100644 index 000000000..717944dc3 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestFitnessData.java @@ -0,0 +1,502 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCrypto; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestFitnessData { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testMessageCountRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + int startSleep = 0x00000000; + int endSleep = 0x01020304; + int startStep = 0x01020304; + int endStep = 0x10203040; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlvSleep = new HuaweiTLV() + .put(0x81) + .put(0x03, startSleep) + .put(0x04, endSleep); + HuaweiTLV expectedTlvStep = new HuaweiTLV() + .put(0x81) + .put(0x03, startStep) + .put(0x04, endStep); + + byte[] sleepSerialized = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x0c, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x32, (byte) 0x69, (byte) 0x7b, (byte) 0x51, (byte) 0x85, (byte) 0x20, (byte) 0x9b, (byte) 0x16, (byte) 0x6b, (byte) 0x93, (byte) 0x8a, (byte) 0x3d, (byte) 0xd5, (byte) 0x9a, (byte) 0xf9, (byte) 0x29, (byte) 0xdf, (byte) 0x07}; + byte[] stepSerialized = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x0a, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x4d, (byte) 0x52, (byte) 0x79, (byte) 0x57, (byte) 0x49, (byte) 0x30, (byte) 0x75, (byte) 0xc6, (byte) 0x28, (byte) 0x5b, (byte) 0x79, (byte) 0xd5, (byte) 0xab, (byte) 0x89, (byte) 0x0d, (byte) 0x1e, (byte) 0xa9, (byte) 0xc9}; + + FitnessData.MessageCount.Request sleepRequest = new FitnessData.MessageCount.Request(secretsProvider, FitnessData.MessageCount.sleepId, startSleep, endSleep); + FitnessData.MessageCount.Request stepRequest = new FitnessData.MessageCount.Request(secretsProvider, FitnessData.MessageCount.stepId, startStep, endStep); + + Assert.assertEquals(0x07, sleepRequest.serviceId); + Assert.assertEquals(0x0c, sleepRequest.commandId); + Assert.assertEquals(expectedTlvSleep, tlvField.get(sleepRequest)); + Assert.assertTrue(sleepRequest.complete); + List outSleep = sleepRequest.serialize(); + Assert.assertEquals(1, outSleep.size()); + Assert.assertArrayEquals(sleepSerialized, outSleep.get(0)); + + Assert.assertEquals(0x07, stepRequest.serviceId); + Assert.assertEquals(0x0a, stepRequest.commandId); + Assert.assertEquals(expectedTlvStep, tlvField.get(stepRequest)); + Assert.assertTrue(stepRequest.complete); + List outStep = stepRequest.serialize(); + Assert.assertEquals(1, outStep.size()); + Assert.assertArrayEquals(stepSerialized, outStep.get(0)); + } + + @Test + public void testMessageCountResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawSleep = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x0c, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xf6, (byte) 0xfb, (byte) 0xc0, (byte) 0xb6, (byte) 0x4f, (byte) 0x9a, (byte) 0xfa, (byte) 0x77, (byte) 0x53, (byte) 0x28, (byte) 0x7d, (byte) 0x13, (byte) 0xca, (byte) 0x49, (byte) 0xda, (byte) 0xfd, (byte) 0x93, (byte) 0x09}; + byte[] rawStep = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x0a, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xf6, (byte) 0xfb, (byte) 0xc0, (byte) 0xb6, (byte) 0x4f, (byte) 0x9a, (byte) 0xfa, (byte) 0x77, (byte) 0x53, (byte) 0x28, (byte) 0x7d, (byte) 0x13, (byte) 0xca, (byte) 0x49, (byte) 0xda, (byte) 0xfd, (byte) 0xd4, (byte) 0x93}; + + short expectedCount = 0x1337; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV().put(0x02, expectedCount)); + + HuaweiPacket packetSleep = new HuaweiPacket(secretsProvider).parse(rawSleep); + HuaweiPacket packetStep = new HuaweiPacket(secretsProvider).parse(rawStep); + packetSleep.parseTlv(); + packetStep.parseTlv(); + + Assert.assertEquals(0x07, packetSleep.serviceId); + Assert.assertEquals(0x0c, packetSleep.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packetSleep)); + Assert.assertTrue(packetSleep.complete); + Assert.assertTrue(packetSleep instanceof FitnessData.MessageCount.Response); + Assert.assertEquals(expectedCount, ((FitnessData.MessageCount.Response) packetSleep).count); + + Assert.assertEquals(0x07, packetStep.serviceId); + Assert.assertEquals(0x0a, packetStep.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packetStep)); + Assert.assertTrue(packetStep.complete); + Assert.assertTrue(packetStep instanceof FitnessData.MessageCount.Response); + Assert.assertEquals(expectedCount, ((FitnessData.MessageCount.Response) packetStep).count); + } + + @Test + public void testMessageDataRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiCrypto.CryptoException, HuaweiPacket.CryptoException { + short count = 0x1337; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV().put(0x02, count)); + expectedTlv.encrypt(secretsProvider); + + byte[] expectedSleep = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x0d, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xf6, (byte) 0xfb, (byte) 0xc0, (byte) 0xb6, (byte) 0x4f, (byte) 0x9a, (byte) 0xfa, (byte) 0x77, (byte) 0x53, (byte) 0x28, (byte) 0x7d, (byte) 0x13, (byte) 0xca, (byte) 0x49, (byte) 0xda, (byte) 0xfd, (byte) 0x7d, (byte) 0xad}; + byte[] expectedStep = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x0b, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xf6, (byte) 0xfb, (byte) 0xc0, (byte) 0xb6, (byte) 0x4f, (byte) 0x9a, (byte) 0xfa, (byte) 0x77, (byte) 0x53, (byte) 0x28, (byte) 0x7d, (byte) 0x13, (byte) 0xca, (byte) 0x49, (byte) 0xda, (byte) 0xfd, (byte) 0x3a, (byte) 0x37}; + + FitnessData.MessageData.Request sleepRequest = new FitnessData.MessageData.Request(secretsProvider, FitnessData.MessageData.sleepId, count); + FitnessData.MessageData.Request stepRequest = new FitnessData.MessageData.Request(secretsProvider, FitnessData.MessageData.stepId, count); + + Assert.assertEquals(0x07, sleepRequest.serviceId); + Assert.assertEquals(0x0d, sleepRequest.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(sleepRequest)); + Assert.assertTrue(sleepRequest.complete); + List outSleep = sleepRequest.serialize(); + Assert.assertEquals(1, outSleep.size()); + Assert.assertArrayEquals(expectedSleep, outSleep.get(0)); + + Assert.assertEquals(0x07, stepRequest.serviceId); + Assert.assertEquals(0x0b, stepRequest.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(stepRequest)); + Assert.assertTrue(stepRequest.complete); + List outStep = stepRequest.serialize(); + Assert.assertEquals(1, outStep.size()); + Assert.assertArrayEquals(expectedStep, outStep.get(0)); + } + + @Test + public void testMessageDataSleepResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x07, (byte) 0x0d, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x20, (byte) 0xa4, (byte) 0x9e, (byte) 0xd8, (byte) 0xd3, (byte) 0x7a, (byte) 0x0e, (byte) 0x51, (byte) 0x55, (byte) 0xc5, (byte) 0x48, (byte) 0x07, (byte) 0x99, (byte) 0xf5, (byte) 0x99, (byte) 0x48, (byte) 0x3e, (byte) 0x41, (byte) 0xed, (byte) 0x16, (byte) 0xf1, (byte) 0x52, (byte) 0xd2, (byte) 0x9f, (byte) 0x38, (byte) 0xe8, (byte) 0xb1, (byte) 0x83, (byte) 0xd6, (byte) 0xcb, (byte) 0x52, (byte) 0xb0, (byte) 0x9f, (byte) 0x48, (byte) 0x05}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, (short) 0x1337) + .put(0x83, new HuaweiTLV() + .put(0x04, (byte) 0x00) + .put(0x05, new byte[] {}) + ) + .put(0x83, new HuaweiTLV() + .put(0x04, (byte) 0x01) + .put(0x05, new byte[] {0x01, 0x02}) + ) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x07, packet.serviceId); + Assert.assertEquals(0x0d, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof FitnessData.MessageData.SleepResponse); + Assert.assertEquals(0x1337, ((FitnessData.MessageData.SleepResponse) packet).number); + Assert.assertEquals(2, ((FitnessData.MessageData.SleepResponse) packet).containers.size()); + Assert.assertEquals(0x00, ((FitnessData.MessageData.SleepResponse) packet).containers.get(0).type); + Assert.assertArrayEquals(new byte[] {}, ((FitnessData.MessageData.SleepResponse) packet).containers.get(0).timestamp); + Assert.assertEquals(0x01, ((FitnessData.MessageData.SleepResponse) packet).containers.get(1).type); + Assert.assertArrayEquals(new byte[] {0x01, 0x02}, ((FitnessData.MessageData.SleepResponse) packet).containers.get(1).timestamp); + } + + @Test + public void testMessageDataStepResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x5a, (byte) 0x00, (byte) 0x07, (byte) 0x0b, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x40, (byte) 0xdc, (byte) 0xb7, (byte) 0xf6, (byte) 0xaa, (byte) 0xb2, (byte) 0xf1, (byte) 0x03, (byte) 0x53, (byte) 0x25, (byte) 0x39, (byte) 0xe4, (byte) 0x79, (byte) 0xdd, (byte) 0xbf, (byte) 0x18, (byte) 0x7b, (byte) 0x98, (byte) 0x30, (byte) 0xb7, (byte) 0x4c, (byte) 0x33, (byte) 0xd2, (byte) 0x0c, (byte) 0xa5, (byte) 0xee, (byte) 0xfe, (byte) 0x5f, (byte) 0xa5, (byte) 0x12, (byte) 0x20, (byte) 0xec, (byte) 0x79, (byte) 0x38, (byte) 0xec, (byte) 0x9e, (byte) 0x4d, (byte) 0xfc, (byte) 0xc3, (byte) 0x5c, (byte) 0x59, (byte) 0x67, (byte) 0x51, (byte) 0x4b, (byte) 0xef, (byte) 0x50, (byte) 0x48, (byte) 0xb7, (byte) 0xf8, (byte) 0xc7, (byte) 0xe3, (byte) 0xf7, (byte) 0xdf, (byte) 0x82, (byte) 0xb4, (byte) 0x1a, (byte) 0xb8, (byte) 0x94, (byte) 0x78, (byte) 0x0d, (byte) 0xda, (byte) 0x53, (byte) 0xe3, (byte) 0xbe, (byte) 0xbf, (byte) 0x21, (byte) 0xc2}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + // TODO: add HR data + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, (short) 0x1337) + .put(0x03, 0xCAFEBEEF) + .put(0x084, new HuaweiTLV() + .put(0x05, (byte) 0x00) + .put(0x06, new byte[] {}) + ) + .put(0x84, new HuaweiTLV() + .put(0x05, (byte) 0x01) + .put(0x06, new byte[] {0x01, 0x02}) + ) + .put(0x84, new HuaweiTLV() + .put(0x05, (byte) 0x02) + .put(0x06, new byte[] {0x0e, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03}) + ) + .put(0x84, new HuaweiTLV() + .put(0x05, (byte) 0x02) + .put(0x06, new byte[] {0x01, 0x00, 0x01}) + ) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x07, packet.serviceId); + Assert.assertEquals(0x0b, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof FitnessData.MessageData.StepResponse); + + Assert.assertEquals(0x1337, ((FitnessData.MessageData.StepResponse) packet).number); + Assert.assertEquals(0xCAFEBEEF, ((FitnessData.MessageData.StepResponse) packet).timestamp); + Assert.assertEquals(4, ((FitnessData.MessageData.StepResponse) packet).containers.size()); + + Assert.assertEquals(0x00, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).timestampOffset); + Assert.assertArrayEquals(new byte[] {}, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).data); + Assert.assertEquals(0xCAFEBEEF, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).timestamp); + Assert.assertNull(((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData); + Assert.assertEquals("Data is missing feature bitmap.", ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedDataError); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).steps); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).calories); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).distance); + Assert.assertNull(((FitnessData.MessageData.StepResponse) packet).containers.get(0).unknownTVs); + + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).timestampOffset); + Assert.assertArrayEquals(new byte[] {0x01, 0x02}, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).data); + Assert.assertEquals(0xCAFEBF2B, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).timestamp); + Assert.assertNull(((FitnessData.MessageData.StepResponse) packet).containers.get(1).parsedData); + Assert.assertEquals("Data is too short for selected features.", ((FitnessData.MessageData.StepResponse) packet).containers.get(1).parsedDataError); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).steps); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).calories); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).distance); + Assert.assertEquals(0, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).unknownTVs.size()); + + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).timestampOffset); + Assert.assertArrayEquals(new byte[] {0x0e, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03}, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).data); + Assert.assertEquals(0xCAFEBF67, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).timestamp); + Assert.assertEquals(3, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.size()); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(0).bitmap); + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(0).tag); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(0).value); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(1).bitmap); + Assert.assertEquals(0x04, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(1).tag); + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(1).value); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(2).bitmap); + Assert.assertEquals(0x08, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(2).tag); + Assert.assertEquals(0x03, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedData.get(2).value); + Assert.assertEquals("", ((FitnessData.MessageData.StepResponse) packet).containers.get(2).parsedDataError); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).steps); + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).calories); + Assert.assertEquals(0x03, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).distance); + Assert.assertEquals(0, ((FitnessData.MessageData.StepResponse) packet).containers.get(2).unknownTVs.size()); + + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).timestampOffset); + Assert.assertArrayEquals(new byte[] {0x01, 0x00, 0x01}, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).data); + Assert.assertEquals(0xCAFEBF67, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).timestamp); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).parsedData.size()); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).parsedData.get(0).bitmap); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).parsedData.get(0).tag); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).parsedData.get(0).value); + Assert.assertEquals("", ((FitnessData.MessageData.StepResponse) packet).containers.get(3).parsedDataError); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).steps); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).calories); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).distance); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).unknownTVs.size()); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).unknownTVs.get(0).bitmap); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).unknownTVs.get(0).tag); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(3).unknownTVs.get(0).value); + } + + @Test + public void testMessageDataStepResponseSingleByte() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x07, (byte) 0x0b, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x20, (byte) 0xcf, (byte) 0xa7, (byte) 0x76, (byte) 0x30, (byte) 0x69, (byte) 0xa3, (byte) 0x83, (byte) 0x6e, (byte) 0xd2, (byte) 0x84, (byte) 0x70, (byte) 0xc8, (byte) 0xca, (byte) 0x94, (byte) 0x87, (byte) 0xd2, (byte) 0x0d, (byte) 0x1e, (byte) 0xf5, (byte) 0x60, (byte) 0x72, (byte) 0xa4, (byte) 0xd9, (byte) 0x8f, (byte) 0xf6, (byte) 0xdf, (byte) 0x09, (byte) 0x35, (byte) 0x3c, (byte) 0x86, (byte) 0x62, (byte) 0x00, (byte) 0x0a, (byte) 0x3b}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + // TODO: change test as 0x40 is now added as HR + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, (short) 0x01) + .put(0x03, 0x02) + .put(0x84, new HuaweiTLV() + .put(0x05, (byte) 0x00) + .put(0x06, new byte[] {0x20, 0x01}) + ) + .put(0x84, new HuaweiTLV() + .put(0x05, (byte) 0x01) + .put(0x06, new byte[] {0x40, 0x02}) + ) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x07, packet.serviceId); + Assert.assertEquals(0x0b, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof FitnessData.MessageData.StepResponse); + + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).number); + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).timestamp); + Assert.assertEquals(2, ((FitnessData.MessageData.StepResponse) packet).containers.size()); + + Assert.assertEquals(0x00, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).timestampOffset); + Assert.assertArrayEquals(new byte[] {0x20, 0x01}, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).data); + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).timestamp); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.size()); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.get(0).bitmap); + Assert.assertEquals(0x20, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.get(0).tag); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.get(0).value); + Assert.assertEquals("", ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedDataError); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).steps); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).calories); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).distance); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).unknownTVs.size()); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).unknownTVs.get(0).bitmap); + Assert.assertEquals(0x20, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).unknownTVs.get(0).tag); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).unknownTVs.get(0).value); + + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).timestampOffset); + Assert.assertArrayEquals(new byte[] {0x40, 0x02}, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).data); + Assert.assertEquals(0x3e, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).timestamp); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).parsedData.size()); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).parsedData.get(0).bitmap); + Assert.assertEquals(0x40, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).parsedData.get(0).tag); + Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).parsedData.get(0).value); + Assert.assertEquals("", ((FitnessData.MessageData.StepResponse) packet).containers.get(1).parsedDataError); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).steps); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).calories); + Assert.assertEquals(-1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).distance); + Assert.assertEquals(0, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).unknownTVs.size()); +// Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).unknownTVs.get(0).bitmap); +// Assert.assertEquals(0x40, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).unknownTVs.get(0).tag); +// Assert.assertEquals(0x02, ((FitnessData.MessageData.StepResponse) packet).containers.get(1).unknownTVs.get(0).value); + } + + @SuppressWarnings("ConstantConditions") + @Test + public void testActivityReminderRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException, HuaweiCrypto.CryptoException { + boolean longSitSwitch = false; + byte longSitInterval = 0x00; + byte[] longSitStart = {0x01, 0x02}; + byte[] longSitEnd = {0x03, 0x04}; + byte cycle = 0x05; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x81, new HuaweiTLV() + .put(0x02, longSitSwitch) + .put(0x03, longSitInterval) + .put(0x04, longSitStart) + .put(0x05, longSitEnd) + .put(0x06, cycle) + ); + expectedTlv.encrypt(secretsProvider); + + byte[] expected = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x07, (byte) 0x07, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x20, (byte) 0x5b, (byte) 0x9b, (byte) 0x16, (byte) 0xa8, (byte) 0x65, (byte) 0x81, (byte) 0xc1, (byte) 0x18, (byte) 0x2f, (byte) 0x42, (byte) 0xab, (byte) 0xf3, (byte) 0x43, (byte) 0x1e, (byte) 0x5c, (byte) 0x32, (byte) 0x9a, (byte) 0xa9, (byte) 0xa2, (byte) 0x18, (byte) 0x36, (byte) 0xb3, (byte) 0x60, (byte) 0x39, (byte) 0xeb, (byte) 0xdb, (byte) 0x6b, (byte) 0xe5, (byte) 0xac, (byte) 0x7b, (byte) 0x45, (byte) 0x36, (byte) 0xbc, (byte) 0x0c}; + + FitnessData.ActivityReminder.Request request = new FitnessData.ActivityReminder.Request( + secretsProvider, + longSitSwitch, + longSitInterval, + longSitStart, + longSitEnd, + cycle + ); + + Assert.assertEquals(0x07, request.serviceId); + Assert.assertEquals(0x07, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expected, out.get(0)); + } + + @SuppressWarnings("ConstantConditions") + @Test + public void testTruSleepRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiCrypto.CryptoException, HuaweiPacket.CryptoException { + boolean truSleepSwitch = false; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, truSleepSwitch); + expectedTlv.encrypt(secretsProvider); + + byte [] expected = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x16, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x28, (byte) 0x00, (byte) 0x99, (byte) 0x6f, (byte) 0x2a, (byte) 0xcb, (byte) 0x62, (byte) 0x3a, (byte) 0xe6, (byte) 0x54, (byte) 0x28, (byte) 0x54, (byte) 0xf8, (byte) 0xab, (byte) 0x54, (byte) 0x83, (byte) 0x02, (byte) 0x23}; + + FitnessData.TruSleep.Request request = new FitnessData.TruSleep.Request(secretsProvider, truSleepSwitch); + + Assert.assertEquals(0x07, request.serviceId); + Assert.assertEquals(0x16, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expected, out.get(0)); + } + + @Test + public void testMessageDataStepResponseNoCount() throws NoSuchFieldException, IllegalAccessException { + // I've seen this happening because of a bug in the counts, so it's probably best to stop the sync if this happens. + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x07, (byte) 0x0b, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x4c, (byte) 0xdd, (byte) 0x99, (byte) 0x79, (byte) 0xf6, (byte) 0x3c, (byte) 0x1e, (byte) 0xbb, (byte) 0x0a, (byte) 0x95, (byte) 0x8d, (byte) 0x12, (byte) 0x05, (byte) 0x81, (byte) 0x7f, (byte) 0xff, (byte) 0xeb, (byte) 0x45}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + try { + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + Assert.fail(); + } catch (HuaweiPacket.ParseException e) { + if (e instanceof HuaweiPacket.MissingTagException) { + Assert.assertNotNull(e.getMessage()); + if (!e.getMessage().equals("Missing tag: 2")) { + Assert.fail(); + } + } else { + Assert.fail(); + } + } + } + + @Test + public void testSpoData() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = {(byte) 0x5a, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x07, (byte) 0x0b, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x20, (byte) 0x30, (byte) 0xdf, (byte) 0x42, (byte) 0xc9, (byte) 0x79, (byte) 0x91, (byte) 0x36, (byte) 0x3d, (byte) 0x80, (byte) 0x6b, (byte) 0x99, (byte) 0xd3, (byte) 0x3f, (byte) 0xbf, (byte) 0x1f, (byte) 0x1e, (byte) 0xc1, (byte) 0x0b, (byte) 0xbf, (byte) 0xcd, (byte) 0xae, (byte) 0x38, (byte) 0x89, (byte) 0x60, (byte) 0x60, (byte) 0xf7, (byte) 0x93, (byte) 0x84, (byte) 0x3a, (byte) 0x09, (byte) 0xd3, (byte) 0x77, (byte) 0x1e, (byte) 0xb9}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, (short) 0x1337) + .put(0x03, 0xCAFEBEEF) + .put(0x84, new HuaweiTLV() + .put(0x05, (byte) 0x00) + .put(0x06, new byte[] {(byte) 0x80, 0x01, 0x42}) + ) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x07, packet.serviceId); + Assert.assertEquals(0x0b, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof FitnessData.MessageData.StepResponse); + + Assert.assertEquals(0x1337, ((FitnessData.MessageData.StepResponse) packet).number); + Assert.assertEquals(0xCAFEBEEF, ((FitnessData.MessageData.StepResponse) packet).timestamp); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.size()); + + Assert.assertEquals(0x00, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).timestampOffset); + Assert.assertArrayEquals(new byte[] {(byte) 0x80, 0x01, 0x42}, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).data); + Assert.assertEquals(0xCAFEBEEF, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).timestamp); + Assert.assertEquals(1, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.size()); + Assert.assertEquals(2, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.get(0).bitmap); + Assert.assertEquals(0x01, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.get(0).tag); + Assert.assertEquals(0x42, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedData.get(0).value); + Assert.assertEquals("", ((FitnessData.MessageData.StepResponse) packet).containers.get(0).parsedDataError); + Assert.assertEquals(0, ((FitnessData.MessageData.StepResponse) packet).containers.get(0).unknownTVs.size()); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestLocaleConfig.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestLocaleConfig.java new file mode 100644 index 000000000..a8b288cc3 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestLocaleConfig.java @@ -0,0 +1,87 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestLocaleConfig { + + HuaweiPacket.ParamsProvider paramsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testSetLocaleRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, new byte[] {0x45, 0x4e, 0x2d, 0x47, 0x42}) + .put(0x02, (byte) 0x00); + + byte[] serialized = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x0c, (byte) 0x01, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x4e, (byte) 0xb0, (byte) 0x71, (byte) 0x05, (byte) 0x7b, (byte) 0xf1, (byte) 0x07, (byte) 0x31, (byte) 0xc4, (byte) 0x6c, (byte) 0x5b, (byte) 0x6d, (byte) 0xbf, (byte) 0x07, (byte) 0xf5, (byte) 0x55, (byte) 0x65, (byte) 0x06}; + + LocaleConfig.SetLanguageSetting request = new LocaleConfig.SetLanguageSetting( + paramsProvider, + new byte[] {0x45, 0x4e, 0x2d, 0x47, 0x42}, + (byte) 0x00 + ); + + Assert.assertEquals(0x0c, request.serviceId); + Assert.assertEquals(0x01, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(serialized, out.get(0)); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestMusicControl.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestMusicControl.java new file mode 100644 index 000000000..e45bf134e --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestMusicControl.java @@ -0,0 +1,338 @@ +/* Copyright (C) 2022-2023 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl.Control.Response.Button; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestMusicControl { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testMusicStatusRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + int okInput = 0x000186a0; + int errInput = 0x00000000; + + byte commandId1 = 0x01; + byte commandId2 = 0x02; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV okExpectedTlv = new HuaweiTLV() + .put(0x7f, okInput); + HuaweiTLV errExpectedTlv = new HuaweiTLV() + .put(0x7f, errInput); + + MusicControl.MusicStatusRequest okRequest = new MusicControl.MusicStatusRequest(secretsProvider, commandId1, okInput); + MusicControl.MusicStatusRequest errRequest = new MusicControl.MusicStatusRequest(secretsProvider, commandId2, errInput); + + Assert.assertEquals(0x25, okRequest.serviceId); + Assert.assertEquals(commandId1, okRequest.commandId); + Assert.assertEquals(okExpectedTlv, tlvField.get(okRequest)); + Assert.assertTrue(okRequest.complete); + + // To check it doesn't error + okRequest.serialize(); + + Assert.assertEquals(0x25, errRequest.serviceId); + Assert.assertEquals(commandId2, errRequest.commandId); + Assert.assertEquals(errExpectedTlv, tlvField.get(errRequest)); + Assert.assertTrue(errRequest.complete); + + // To check it doesn't error + errRequest.serialize(); + } + + @Test + public void testMusicStatusResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x25, (byte) 0x01, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x01, (byte) 0x43, (byte) 0xdb, (byte) 0x63, (byte) 0xee, (byte) 0x66, (byte) 0xb0, (byte) 0xcd, (byte) 0xff, (byte) 0x9f, (byte) 0x69, (byte) 0x91, (byte) 0x76, (byte) 0x80, (byte) 0x15, (byte) 0x1e, (byte) 0x52, (byte) 0x46}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV(); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x25, packet.serviceId); + Assert.assertEquals(0x01, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet instanceof MusicControl.MusicStatusResponse); + + // TODO: complete test when more is known about packet contents + } + + @Test + public void testMusicStatusResponseUnencrypted() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawOk = new byte[] {0x5a, 0x00, 0x09, 0x00, 0x25, 0x01, 0x7f, 0x04, 0x00, 0x01, (byte) 0x86, (byte) 0xa0, 0x63, (byte) 0x96}; + byte[] rawErr = new byte[] {0x5a, 0x00, 0x09, 0x00, 0x25, 0x01, 0x7f, 0x04, 0x00, 0x01, (byte) 0x86, (byte) 0xaa, (byte) 0xc2, (byte) 0xdc}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlvOk = new HuaweiTLV().put(0x7F, 0x000186A0); + HuaweiTLV expectedTlvErr = new HuaweiTLV().put(0x7F, 0x000186AA); + + HuaweiPacket packetOk = new HuaweiPacket(secretsProvider).parse(rawOk); + HuaweiPacket packetErr = new HuaweiPacket(secretsProvider).parse(rawErr); + packetOk.parseTlv(); + packetErr.parseTlv(); + + Assert.assertEquals(0x25, packetOk.serviceId); + Assert.assertEquals(0x01, packetOk.commandId); + Assert.assertEquals(expectedTlvOk, tlvField.get(packetOk)); + Assert.assertTrue(packetOk instanceof MusicControl.MusicStatusResponse); + Assert.assertEquals(0x000186A0, ((MusicControl.MusicStatusResponse) packetOk).status); + + Assert.assertEquals(0x25, packetErr.serviceId); + Assert.assertEquals(0x01, packetErr.commandId); + Assert.assertEquals(expectedTlvErr, tlvField.get(packetErr)); + Assert.assertTrue(packetErr instanceof MusicControl.MusicStatusResponse); + Assert.assertEquals(0x000186AA, ((MusicControl.MusicStatusResponse) packetErr).status); + } + + @Test + public void testMusicInfoRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + String artistName = "Artist"; + String songName = "Song"; + byte playState = 0x01; + byte maxVolume = 0x03; + byte currentVolume = 0x02; + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, artistName) + .put(0x02, songName) + .put(0x03, playState) + .put(0x04, maxVolume) + .put(0x05, currentVolume); + byte[] expectedSerializedPacket = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x25, (byte) 0x02, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x20, (byte) 0x21, (byte) 0x8b, (byte) 0xe1, (byte) 0x3d, (byte) 0x9f, (byte) 0x85, (byte) 0xd2, (byte) 0x2e, (byte) 0x64, (byte) 0x87, (byte) 0x3f, (byte) 0x1d, (byte) 0xab, (byte) 0x3f, (byte) 0xc7, (byte) 0x39, (byte) 0xb6, (byte) 0x34, (byte) 0x89, (byte) 0x60, (byte) 0xa0, (byte) 0x36, (byte) 0x4a, (byte) 0x08, (byte) 0x7a, (byte) 0x16, (byte) 0xed, (byte) 0xc9, (byte) 0x9e, (byte) 0xf3, (byte) 0xbf, (byte) 0x44, (byte) 0xac, (byte) 0x58}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + MusicControl.MusicInfo.Request musicInfoRequest = new MusicControl.MusicInfo.Request( + secretsProvider, + artistName, + songName, + playState, + maxVolume, + currentVolume + ); + + Assert.assertEquals(0x25, musicInfoRequest.serviceId); + Assert.assertEquals(0x02, musicInfoRequest.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(musicInfoRequest)); + Assert.assertTrue(musicInfoRequest.complete); + List out = musicInfoRequest.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expectedSerializedPacket, out.get(0)); + } + + @Test + public void testMusicInfoResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] rawOk = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x25, (byte) 0x02, (byte) 0x7f, (byte) 0x04, (byte) 0x00, (byte) 0x01, (byte) 0x86, (byte) 0xA0, (byte) 0xbb, (byte) 0x14}; + byte[] rawErr = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x25, (byte) 0x02, (byte) 0x7f, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x88, (byte) 0xf0}; + byte[] rawMissing = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x25, (byte) 0x02, (byte) 0xb4, (byte) 0x1b}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV okExpectedTlv = new HuaweiTLV() + .put(0x7f, 0x000186a0); + HuaweiTLV errExpectedTlv = new HuaweiTLV() + .put(0x7f, 0x00000000); + HuaweiTLV missingExpectedTlv = new HuaweiTLV(); + + HuaweiPacket packetOk = new HuaweiPacket(secretsProvider).parse(rawOk); + HuaweiPacket packetErr = new HuaweiPacket(secretsProvider).parse(rawErr); + HuaweiPacket packetMissing = new HuaweiPacket(secretsProvider).parse(rawMissing); + + packetOk.parseTlv(); + packetErr.parseTlv(); + packetMissing.parseTlv(); + + Assert.assertEquals(0x25, packetOk.serviceId); + Assert.assertEquals(0x02, packetOk.commandId); + Assert.assertEquals(okExpectedTlv, tlvField.get(packetOk)); + Assert.assertTrue(packetOk instanceof MusicControl.MusicInfo.Response); + Assert.assertTrue(((MusicControl.MusicInfo.Response) packetOk).ok); + Assert.assertEquals("", ((MusicControl.MusicInfo.Response) packetOk).error); + + Assert.assertEquals(0x25, packetErr.serviceId); + Assert.assertEquals(0x02, packetErr.commandId); + Assert.assertEquals(errExpectedTlv, tlvField.get(packetErr)); + Assert.assertTrue(packetErr instanceof MusicControl.MusicInfo.Response); + Assert.assertFalse(((MusicControl.MusicInfo.Response) packetErr).ok); + Assert.assertEquals("Music information error code: 0", ((MusicControl.MusicInfo.Response) packetErr).error); + + Assert.assertEquals(0x25, packetMissing.serviceId); + Assert.assertEquals(0x02, packetMissing.commandId); + Assert.assertEquals(missingExpectedTlv, tlvField.get(packetMissing)); + Assert.assertTrue(packetMissing instanceof MusicControl.MusicInfo.Response); + Assert.assertFalse(((MusicControl.MusicInfo.Response) packetMissing).ok); + Assert.assertEquals("Music information response no status tag", ((MusicControl.MusicInfo.Response) packetMissing).error); + } + + @Test + public void testControlResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] emptyInput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x25, (byte) 0x03, (byte) 0xa4, (byte) 0x3a, }; + byte[] playPauseInput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x25, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xe6, (byte) 0x85}; + byte[] previousInput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x25, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0xc6, (byte) 0xc7}; + byte[] nextInput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x25, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0xb6, (byte) 0x20}; + byte[] unknownButtonInput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x25, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xFF, (byte) 0xe8, (byte) 0x54}; + byte[] volumeInput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x25, (byte) 0x03, (byte) 0x02, (byte) 0x01, (byte) 0x42, (byte) 0xc7, (byte) 0x72}; + byte[] combinedInput = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x25, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x42, (byte) 0x95, (byte) 0x9a}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV emptyExpectedTlv = new HuaweiTLV(); + HuaweiTLV playPauseExpectedTlv = new HuaweiTLV() + .put(0x01, (byte) 0x01); + HuaweiTLV previousExpectedTlv = new HuaweiTLV() + .put(0x01, (byte) 0x03); + HuaweiTLV nextExpectedTlv = new HuaweiTLV() + .put(0x01, (byte) 0x04); + HuaweiTLV unknownButtonExpectedTlv = new HuaweiTLV() + .put(0x01, (byte) 0xFF); + HuaweiTLV volumeExpectedTlv = new HuaweiTLV() + .put(0x02, (byte) 0x42); + HuaweiTLV combinedExpectedTlv = new HuaweiTLV() + .put(0x01, (byte) 0x01) + .put(0x02, (byte) 0x42); + + HuaweiPacket emptyResponse = new HuaweiPacket(secretsProvider).parse(emptyInput); + HuaweiPacket playPauseResponse = new HuaweiPacket(secretsProvider).parse(playPauseInput); + HuaweiPacket previousResponse = new HuaweiPacket(secretsProvider).parse(previousInput); + HuaweiPacket nextResponse = new HuaweiPacket(secretsProvider).parse(nextInput); + HuaweiPacket unknownButtonResponse = new HuaweiPacket(secretsProvider).parse(unknownButtonInput); + HuaweiPacket volumeResponse = new HuaweiPacket(secretsProvider).parse(volumeInput); + HuaweiPacket combinedResponse = new HuaweiPacket(secretsProvider).parse(combinedInput); + + emptyResponse.parseTlv(); + playPauseResponse.parseTlv(); + previousResponse.parseTlv(); + nextResponse.parseTlv(); + unknownButtonResponse.parseTlv(); + volumeResponse.parseTlv(); + combinedResponse.parseTlv(); + + // TODO: play and pause are now split - test needs to be updated + + Assert.assertEquals(0x25, emptyResponse.serviceId); + Assert.assertEquals(0x03, emptyResponse.commandId); + Assert.assertEquals(emptyExpectedTlv, tlvField.get(emptyResponse)); + Assert.assertTrue(emptyResponse instanceof MusicControl.Control.Response); + Assert.assertFalse(((MusicControl.Control.Response) emptyResponse).buttonPresent); + Assert.assertFalse(((MusicControl.Control.Response) emptyResponse).volumePresent); + + Assert.assertEquals(0x25, playPauseResponse.serviceId); + Assert.assertEquals(0x03, playPauseResponse.commandId); + Assert.assertEquals(playPauseExpectedTlv, tlvField.get(playPauseResponse)); + Assert.assertTrue(playPauseResponse instanceof MusicControl.Control.Response); + Assert.assertTrue(((MusicControl.Control.Response) playPauseResponse).buttonPresent); + Assert.assertEquals(0x01, ((MusicControl.Control.Response) playPauseResponse).rawButton); + // Assert.assertEquals(Button.PlayPause, ((MusicControl.Control.Response) playPauseResponse).button); + Assert.assertFalse(((MusicControl.Control.Response) playPauseResponse).volumePresent); + + Assert.assertEquals(0x25, previousResponse.serviceId); + Assert.assertEquals(0x03, previousResponse.commandId); + Assert.assertEquals(previousExpectedTlv, tlvField.get(previousResponse)); + Assert.assertTrue(previousResponse instanceof MusicControl.Control.Response); + Assert.assertTrue(((MusicControl.Control.Response) previousResponse).buttonPresent); + Assert.assertEquals(0x03, ((MusicControl.Control.Response) previousResponse).rawButton); + Assert.assertEquals(Button.Previous, ((MusicControl.Control.Response) previousResponse).button); + Assert.assertFalse(((MusicControl.Control.Response) previousResponse).volumePresent); + + Assert.assertEquals(0x25, nextResponse.serviceId); + Assert.assertEquals(0x03, nextResponse.commandId); + Assert.assertEquals(nextExpectedTlv, tlvField.get(nextResponse)); + Assert.assertTrue(nextResponse instanceof MusicControl.Control.Response); + Assert.assertTrue(((MusicControl.Control.Response) nextResponse).buttonPresent); + Assert.assertEquals(0x04, ((MusicControl.Control.Response) nextResponse).rawButton); + Assert.assertEquals(Button.Next, ((MusicControl.Control.Response) nextResponse).button); + Assert.assertFalse(((MusicControl.Control.Response) nextResponse).volumePresent); + + Assert.assertEquals(0x25, unknownButtonResponse.serviceId); + Assert.assertEquals(0x03, unknownButtonResponse.commandId); + Assert.assertEquals(unknownButtonExpectedTlv, tlvField.get(unknownButtonResponse)); + Assert.assertTrue(unknownButtonResponse instanceof MusicControl.Control.Response); + Assert.assertTrue(((MusicControl.Control.Response) unknownButtonResponse).buttonPresent); + Assert.assertEquals((byte) 0xFF, ((MusicControl.Control.Response) unknownButtonResponse).rawButton); + Assert.assertEquals(Button.Unknown, ((MusicControl.Control.Response) unknownButtonResponse).button); + Assert.assertFalse(((MusicControl.Control.Response) unknownButtonResponse).volumePresent); + + Assert.assertEquals(0x25, volumeResponse.serviceId); + Assert.assertEquals(0x03, volumeResponse.commandId); + Assert.assertEquals(volumeExpectedTlv, tlvField.get(volumeResponse)); + Assert.assertTrue(volumeResponse instanceof MusicControl.Control.Response); + Assert.assertFalse(((MusicControl.Control.Response) volumeResponse).buttonPresent); + Assert.assertTrue(((MusicControl.Control.Response) volumeResponse).volumePresent); + Assert.assertEquals(0x42, ((MusicControl.Control.Response) volumeResponse).volume); + + Assert.assertEquals(0x25, combinedResponse.serviceId); + Assert.assertEquals(0x03, combinedResponse.commandId); + Assert.assertEquals(combinedExpectedTlv, tlvField.get(combinedResponse)); + Assert.assertTrue(combinedResponse instanceof MusicControl.Control.Response); + Assert.assertTrue(((MusicControl.Control.Response) combinedResponse).buttonPresent); + Assert.assertEquals(0x01, ((MusicControl.Control.Response) combinedResponse).rawButton); + // Assert.assertEquals(Button.PlayPause, ((MusicControl.Control.Response) combinedResponse).button); + Assert.assertTrue(((MusicControl.Control.Response) combinedResponse).volumePresent); + Assert.assertEquals(0x42, ((MusicControl.Control.Response) combinedResponse).volume); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestNotifications.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestNotifications.java new file mode 100644 index 000000000..fb42a7c8e --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestNotifications.java @@ -0,0 +1,199 @@ +/* Copyright (C) 2022-2023 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestNotifications { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testNotificationActionRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + short notificationId = 0x01; + byte notificationType = 0x02; + byte titleEncoding = 0x02; + String titleContent = "Title"; + byte senderEncoding = 0x02; + String senderContent = "Sender"; + byte bodyEncoding = 0x02; + String bodyContent = "Body"; + String sourceAppId = "SourceApp"; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV() + .put(0x01, notificationId) + .put(0x02, notificationType) + .put(0x03, true) + .put(0x84, new HuaweiTLV() + .put(0x8C, new HuaweiTLV() + .put(0x8D, new HuaweiTLV() + .put(0x0E, (byte) 0x03) + .put(0x0F, titleEncoding) + .put(0x10, titleContent) + ) + .put(0x8D, new HuaweiTLV() + .put(0x0E, (byte) 0x02) + .put(0x0F, senderEncoding) + .put(0x10, senderContent) + ) + .put(0x8D, new HuaweiTLV() + .put(0x0E, (byte) 0x01) + .put(0x0F, bodyEncoding) + .put(0x10, bodyContent) + ) + ) + ) + .put(0x11, sourceAppId); + + Notifications.NotificationActionRequest request = new Notifications.NotificationActionRequest( + secretsProvider, + notificationId, + notificationType, + titleEncoding, + titleContent, + senderEncoding, + senderContent, + bodyEncoding, + bodyContent, + sourceAppId + ); + + Assert.assertEquals(0x02, request.serviceId); + Assert.assertEquals(0x01, request.commandId); + Assert.assertTrue(request.complete); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + + // Only check that this doesn't error + request.serialize(); + } + + @Test + public void testSetNotificationRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlvTrue = new HuaweiTLV() + .put(0x81, new HuaweiTLV() + .put(0x02, true) + .put(0x03, true) + ); + + HuaweiTLV expectedTlvFalse = new HuaweiTLV() + .put(0x81, new HuaweiTLV() + .put(0x02, false) + .put(0x03, false) + ); + + byte[] expectedOutputTrue = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x02, (byte) 0x04, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xd9, (byte) 0xc4, (byte) 0xaa, (byte) 0x7d, (byte) 0xa3, (byte) 0x5c, (byte) 0x42, (byte) 0xab, (byte) 0x2d, (byte) 0xc2, (byte) 0xe7, (byte) 0x73, (byte) 0xc0, (byte) 0x4c, (byte) 0x97, (byte) 0x5a, (byte) 0x41, (byte) 0x23}; + byte[] expectedOutputFalse = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x02, (byte) 0x04, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xeb, (byte) 0x1f, (byte) 0x20, (byte) 0x0a, (byte) 0x7d, (byte) 0xe2, (byte) 0x25, (byte) 0x45, (byte) 0x01, (byte) 0x5b, (byte) 0xe8, (byte) 0x24, (byte) 0xe3, (byte) 0x7e, (byte) 0x1d, (byte) 0x9c, (byte) 0x47, (byte) 0x31}; + + Notifications.NotificationStateRequest requestTrue = new Notifications.NotificationStateRequest(secretsProvider, true); + Notifications.NotificationStateRequest requestFalse = new Notifications.NotificationStateRequest(secretsProvider, false); + + Assert.assertEquals(0x02, requestTrue.serviceId); + Assert.assertEquals(0x04, requestTrue.commandId); + Assert.assertTrue(requestTrue.complete); + Assert.assertEquals(expectedTlvTrue, tlvField.get(requestTrue)); + List outTrue = requestTrue.serialize(); + Assert.assertEquals(1, outTrue.size()); + Assert.assertArrayEquals(expectedOutputTrue, outTrue.get(0)); + + Assert.assertEquals(0x02, requestFalse.serviceId); + Assert.assertEquals(0x04, requestFalse.commandId); + Assert.assertTrue(requestFalse.complete); + Assert.assertEquals(expectedTlvFalse, tlvField.get(requestFalse)); + List outFalse = requestFalse.serialize(); + Assert.assertEquals(1, outFalse.size()); + Assert.assertArrayEquals(expectedOutputFalse, outFalse.get(0)); + } + + @Test + @Ignore("Broken since https://codeberg.org/psolyca/Gadgetbridge/commit/5b0736b7518aa5c998ac13207fff66286393965b") + public void testSetWearMessagePushRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlvTrue = new HuaweiTLV() + .put(0x01, true); + + HuaweiTLV expectedTlvFalse = new HuaweiTLV() + .put(0x01, false); + + byte[] expectedOutputTrue = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x02, (byte) 0x08, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xcd, (byte) 0x97, (byte) 0x7e, (byte) 0x01, (byte) 0x48, (byte) 0x34, (byte) 0x2a, (byte) 0x48, (byte) 0x58, (byte) 0x0d, (byte) 0x30, (byte) 0xc7, (byte) 0xbc, (byte) 0x2e, (byte) 0x40, (byte) 0xd4, (byte) 0x29, (byte) 0xe0}; + byte[] expectedOutputFalse = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x02, (byte) 0x08, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x28, (byte) 0x00, (byte) 0x99, (byte) 0x6f, (byte) 0x2a, (byte) 0xcb, (byte) 0x62, (byte) 0x3a, (byte) 0xe6, (byte) 0x54, (byte) 0x28, (byte) 0x54, (byte) 0xf8, (byte) 0xab, (byte) 0x54, (byte) 0x83, (byte) 0x30, (byte) 0xd2}; + + Notifications.WearMessagePushRequest requestTrue = new Notifications.WearMessagePushRequest(secretsProvider, true); + Notifications.WearMessagePushRequest requestFalse = new Notifications.WearMessagePushRequest(secretsProvider, false); + + Assert.assertEquals(0x02, requestTrue.serviceId); + Assert.assertEquals(0x08, requestTrue.commandId); + Assert.assertTrue(requestTrue.complete); + Assert.assertEquals(expectedTlvTrue, tlvField.get(requestTrue)); + List outTrue = requestTrue.serialize(); + Assert.assertEquals(1, outTrue.size()); + Assert.assertArrayEquals(expectedOutputTrue, outTrue.get(0)); + + Assert.assertEquals(0x02, requestFalse.serviceId); + Assert.assertEquals(0x08, requestFalse.commandId); + Assert.assertTrue(requestFalse.complete); + Assert.assertEquals(expectedTlvFalse, tlvField.get(requestFalse)); + List outFalse = requestFalse.serialize(); + Assert.assertEquals(1, outFalse.size()); + Assert.assertArrayEquals(expectedOutputFalse, outFalse.get(0)); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestWorkMode.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestWorkMode.java new file mode 100644 index 000000000..2be8d6cf6 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestWorkMode.java @@ -0,0 +1,94 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestWorkMode { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testSwitchStatusRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlvTrue = new HuaweiTLV() + .put(0x01, true); + HuaweiTLV expectedTlvFalse = new HuaweiTLV() + .put(0x01, false); + + byte[] serializedTrue = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x26, (byte) 0x02, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xcd, (byte) 0x97, (byte) 0x7e, (byte) 0x01, (byte) 0x48, (byte) 0x34, (byte) 0x2a, (byte) 0x48, (byte) 0x58, (byte) 0x0d, (byte) 0x30, (byte) 0xc7, (byte) 0xbc, (byte) 0x2e, (byte) 0x40, (byte) 0xd4, (byte) 0x5c, (byte) 0x5a}; + byte[] serializedFalse = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x26, (byte) 0x02, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x28, (byte) 0x00, (byte) 0x99, (byte) 0x6f, (byte) 0x2a, (byte) 0xcb, (byte) 0x62, (byte) 0x3a, (byte) 0xe6, (byte) 0x54, (byte) 0x28, (byte) 0x54, (byte) 0xf8, (byte) 0xab, (byte) 0x54, (byte) 0x83, (byte) 0x45, (byte) 0x68}; + + WorkMode.SwitchStatusRequest requestTrue = new WorkMode.SwitchStatusRequest(secretsProvider, true); + WorkMode.SwitchStatusRequest requestFalse = new WorkMode.SwitchStatusRequest(secretsProvider, false); + + Assert.assertEquals(0x26, requestTrue.serviceId); + Assert.assertEquals(0x02, requestTrue.commandId); + Assert.assertEquals(expectedTlvTrue, tlvField.get(requestTrue)); + Assert.assertTrue(requestTrue.complete); + List outTrue = requestTrue.serialize(); + Assert.assertEquals(1, outTrue.size()); + Assert.assertArrayEquals(serializedTrue, outTrue.get(0)); + + Assert.assertEquals(0x26, requestFalse.serviceId); + Assert.assertEquals(0x02, requestFalse.commandId); + Assert.assertEquals(expectedTlvFalse, tlvField.get(requestFalse)); + Assert.assertTrue(requestFalse.complete); + List outFalse = requestFalse.serialize(); + Assert.assertEquals(1, outFalse.size()); + Assert.assertArrayEquals(serializedFalse, outFalse.get(0)); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestWorkout.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestWorkout.java new file mode 100644 index 000000000..d29b173de --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/devices/huawei/packets/TestWorkout.java @@ -0,0 +1,437 @@ +/* Copyright (C) 2022 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets; + +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; + +public class TestWorkout { + + HuaweiPacket.ParamsProvider secretsProvider = new HuaweiPacket.ParamsProvider() { + @Override + public byte getDeviceSupportType() { + return 0; + } + + @Override + public byte[] getSecretKey() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public byte[] getIv() { + return new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + } + + @Override + public boolean areTransactionsCrypted() { + return true; + } + + @Override + public int getMtu() { + return 0; + } + + @Override + public int getSliceSize() { + return 0xF4; + } + }; + + @Test + public void testWorkoutCountRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + int start = 0x00000000; + int end = 0x01020304; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x03, start) + .put(0x04, end) + ); + + byte[] expected = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x17, (byte) 0x07, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xf7, (byte) 0x48, (byte) 0xf7, (byte) 0x49, (byte) 0x4a, (byte) 0xa5, (byte) 0xb2, (byte) 0xc9, (byte) 0x41, (byte) 0xf5, (byte) 0x7f, (byte) 0xb4, (byte) 0xe9, (byte) 0x17, (byte) 0xac, (byte) 0xb5, (byte) 0x5f, (byte) 0x8e}; + + Workout.WorkoutCount.Request request = new Workout.WorkoutCount.Request(secretsProvider, start, end); + + Assert.assertEquals(0x17, request.serviceId); + Assert.assertEquals(0x07, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expected, out.get(0)); + } + + @Test + public void testWorkoutCountResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x4a, (byte) 0x00, (byte) 0x17, (byte) 0x07, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x30, (byte) 0xee, (byte) 0xdd, (byte) 0xa9, (byte) 0x23, (byte) 0x2c, (byte) 0xe4, (byte) 0x9f, (byte) 0x41, (byte) 0x0b, (byte) 0x9f, (byte) 0x7a, (byte) 0xc2, (byte) 0xe0, (byte) 0x72, (byte) 0x6d, (byte) 0xe1, (byte) 0x8f, (byte) 0xd0, (byte) 0xe7, (byte) 0x41, (byte) 0x59, (byte) 0x38, (byte) 0xac, (byte) 0x17, (byte) 0x66, (byte) 0xc8, (byte) 0x60, (byte) 0xd7, (byte) 0xd2, (byte) 0x32, (byte) 0x8b, (byte) 0xa5, (byte) 0x91, (byte) 0xc7, (byte) 0xc5, (byte) 0xe5, (byte) 0x7d, (byte) 0x8d, (byte) 0xa1, (byte) 0xd0, (byte) 0x6f, (byte) 0xe2, (byte) 0xe2, (byte) 0x24, (byte) 0x7d, (byte) 0xef, (byte) 0x02, (byte) 0x03, (byte) 0x59, (byte) 0x3e}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, (short) 0x1337) + .put(0x85, new HuaweiTLV() + .put(0x06, (short) 0x0001) + .put(0x07, (short) 0x0002) + .put(0x08, (short) 0x0003) + .put(0x0a, (short) 0x0004) + ) + .put(0x85, new HuaweiTLV() + .put(0x06, (short) 0x0005) + .put(0x07, (short) 0x0006) + .put(0x08, (short) 0x0007) + .put(0x0a, (short) 0x0008) + ) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x17, packet.serviceId); + Assert.assertEquals(0x07, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof Workout.WorkoutCount.Response); + Assert.assertEquals(0x1337, ((Workout.WorkoutCount.Response) packet).count); + Assert.assertEquals(2, ((Workout.WorkoutCount.Response) packet).workoutNumbers.size()); + + Assert.assertArrayEquals(new byte[] {0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0x02, 0x08, 0x02, 0x00, 0x03, 0x0a, 0x02, 0x00, 0x04}, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(0).rawData); + Assert.assertEquals(0x01, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(0).workoutNumber); + Assert.assertEquals(0x02, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(0).dataCount); + Assert.assertEquals(0x03, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(0).paceCount); + + Assert.assertArrayEquals(new byte[] {0x06, 0x02, 0x00, 0x05, 0x07, 0x02, 0x00, 0x06, 0x08, 0x02, 0x00, 0x07, 0x0a, 0x02, 0x00, 0x08}, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(1).rawData); + Assert.assertEquals(0x05, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(1).workoutNumber); + Assert.assertEquals(0x06, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(1).dataCount); + Assert.assertEquals(0x07, ((Workout.WorkoutCount.Response) packet).workoutNumbers.get(1).paceCount); + } + + @Test + public void testWorkoutTotalsRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + short number = 0x1337; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, number) + ); + + byte[] expected = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x17, (byte) 0x08, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xf6, (byte) 0xfb, (byte) 0xc0, (byte) 0xb6, (byte) 0x4f, (byte) 0x9a, (byte) 0xfa, (byte) 0x77, (byte) 0x53, (byte) 0x28, (byte) 0x7d, (byte) 0x13, (byte) 0xca, (byte) 0x49, (byte) 0xda, (byte) 0xfd, (byte) 0x26, (byte) 0x91}; + + Workout.WorkoutTotals.Request request = new Workout.WorkoutTotals.Request(secretsProvider, number); + + Assert.assertEquals(0x17, request.serviceId); + Assert.assertEquals(0x08, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expected, out.get(0)); + } + + @Test + public void testWorkoutTotalsResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = new byte[] {(byte) 0x5a, (byte) 0x00, (byte) 0x5a, (byte) 0x00, (byte) 0x17, (byte) 0x08, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x40, (byte) 0x0f, (byte) 0xa0, (byte) 0x3a, (byte) 0x90, (byte) 0xae, (byte) 0x8c, (byte) 0xcf, (byte) 0x03, (byte) 0xce, (byte) 0x5a, (byte) 0x68, (byte) 0x87, (byte) 0x05, (byte) 0x51, (byte) 0xf7, (byte) 0x2f, (byte) 0x78, (byte) 0xbd, (byte) 0x84, (byte) 0xf1, (byte) 0x4f, (byte) 0xb8, (byte) 0x51, (byte) 0x28, (byte) 0xec, (byte) 0xfd, (byte) 0x8b, (byte) 0x2e, (byte) 0x99, (byte) 0xd3, (byte) 0x42, (byte) 0xd7, (byte) 0x65, (byte) 0xb2, (byte) 0x82, (byte) 0x02, (byte) 0x28, (byte) 0x00, (byte) 0x34, (byte) 0xbc, (byte) 0x39, (byte) 0x59, (byte) 0x8f, (byte) 0x0b, (byte) 0xa7, (byte) 0x3a, (byte) 0x5c, (byte) 0xfb, (byte) 0xf1, (byte) 0xd4, (byte) 0x8f, (byte) 0xf6, (byte) 0x6d, (byte) 0x98, (byte) 0xd6, (byte) 0x5a, (byte) 0x51, (byte) 0x0a, (byte) 0x4a, (byte) 0x1c, (byte) 0x42, (byte) 0xc8, (byte) 0x9d, (byte) 0xee, (byte) 0x55, (byte) 0x44}; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, (short) 0x1337) + .put(0x03, (byte) 0x01) + .put(0x04, 0x01020304) + .put(0x05, 0x05060708) + .put(0x06, 0x090a0b0c) + .put(0x07, 0x0d0e0f10) + .put(0x08, 0x11121314) + .put(0x09, 0x15161718) + .put(0x12, 0x191a1b1c) + .put(0x14, (byte) 0x1d) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + // TODO: find out what the status and type can be + + Assert.assertEquals(0x17, packet.serviceId); + Assert.assertEquals(0x08, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof Workout.WorkoutTotals.Response); + Assert.assertArrayEquals(new byte[] {0x02, 0x02, 0x13, 0x37, 0x03, 0x01, 0x01, 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x05, 0x04, 0x05, 0x06, 0x07, 0x08, 0x06, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x07, 0x04, 0x0d, 0x0e, 0x0f, 0x10, 0x08, 0x04, 0x11, 0x12, 0x13, 0x14, 0x09, 0x04, 0x15, 0x16, 0x17, 0x18, 0x12, 0x04, 0x19, 0x1a, 0x1b, 0x1c, 0x14, 0x01, 0x1d}, ((Workout.WorkoutTotals.Response) packet).rawData); + Assert.assertEquals(0x1337, ((Workout.WorkoutTotals.Response) packet).number); + Assert.assertEquals(0x01, ((Workout.WorkoutTotals.Response) packet).status); + Assert.assertEquals(0x01020304, ((Workout.WorkoutTotals.Response) packet).startTime); + Assert.assertEquals(0x05060708, ((Workout.WorkoutTotals.Response) packet).endTime); + Assert.assertEquals(0x090a0b0c, ((Workout.WorkoutTotals.Response) packet).calories); + Assert.assertEquals(0x0d0e0f10, ((Workout.WorkoutTotals.Response) packet).distance); + Assert.assertEquals(0x11121314, ((Workout.WorkoutTotals.Response) packet).stepCount); + Assert.assertEquals(0x15161718, ((Workout.WorkoutTotals.Response) packet).totalTime); + Assert.assertEquals(0x191a1b1c, ((Workout.WorkoutTotals.Response) packet).duration); + Assert.assertEquals(0x1d, ((Workout.WorkoutTotals.Response) packet).type); + } + + @Test + public void testWorkoutDataRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + short workoutNumber = 0x0102; + short dataNumber = 0x0304; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, workoutNumber) + .put(0x03, dataNumber) + ); + + byte[] expected = {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x17, (byte) 0x0a, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0xd2, (byte) 0xd7, (byte) 0x55, (byte) 0x23, (byte) 0xeb, (byte) 0x51, (byte) 0x4f, (byte) 0xe0, (byte) 0x35, (byte) 0x6c, (byte) 0x60, (byte) 0xc5, (byte) 0xbf, (byte) 0x61, (byte) 0x68, (byte) 0xd1, (byte) 0x03, (byte) 0x83}; + + Workout.WorkoutData.Request request = new Workout.WorkoutData.Request(secretsProvider, workoutNumber, dataNumber); + + Assert.assertEquals(0x17, request.serviceId); + Assert.assertEquals(0x0a, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expected, out.get(0)); + } + + @Test + public void testWorkoutDataResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = {(byte) 0x5a, (byte) 0x00, (byte) 0x5a, (byte) 0x00, (byte) 0x17, (byte) 0x0a, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x40, (byte) 0x03, (byte) 0x66, (byte) 0xf5, (byte) 0x16, (byte) 0xc9, (byte) 0x60, (byte) 0xb9, (byte) 0xf2, (byte) 0xe3, (byte) 0x88, (byte) 0x99, (byte) 0xab, (byte) 0x50, (byte) 0x22, (byte) 0xcb, (byte) 0x83, (byte) 0x53, (byte) 0xd0, (byte) 0xb2, (byte) 0xc3, (byte) 0x66, (byte) 0xa9, (byte) 0x16, (byte) 0x23, (byte) 0xa5, (byte) 0x8e, (byte) 0x81, (byte) 0x68, (byte) 0x85, (byte) 0x38, (byte) 0x3e, (byte) 0xd5, (byte) 0x8e, (byte) 0x21, (byte) 0xc8, (byte) 0xa1, (byte) 0x80, (byte) 0x98, (byte) 0x2d, (byte) 0x78, (byte) 0x75, (byte) 0x80, (byte) 0xa1, (byte) 0x39, (byte) 0x61, (byte) 0xa6, (byte) 0x3e, (byte) 0x61, (byte) 0x2c, (byte) 0x5e, (byte) 0xe2, (byte) 0x6f, (byte) 0xef, (byte) 0xdf, (byte) 0xdb, (byte) 0x39, (byte) 0x8f, (byte) 0xab, (byte) 0x21, (byte) 0xde, (byte) 0xba, (byte) 0xdb, (byte) 0x2c, (byte) 0xff, (byte) 0x97, (byte) 0x94}; + + short workoutNumber = 0x0102; + short dataNumber = 0x0304; + + int timestamp = 0x05060708; + byte interval = 0x09; + short dataCount = 0x0002; + byte dataLength = 0x0F; // Data length must match + short bitmap = 0x0042; // Inner data and speed + + short speed1 = 0x0a0b; + short cadence1 = 0x0c0d; + short stepLength1 = 0x0e0f; + short groundContactTime1 = 0x1011; + byte groundImpact1 = 0x12; + short swingAngle1 = 0x1314; + byte foreFootLanding1 = 0x15; + byte midFootLanding1 = 0x16; + byte backFootLanding1 = 0x17; + byte eversionAngle1 = 0x18; + + short speed2 = 0x191a; + short cadence2 = 0x1b1c; + short stepLength2 = 0x1d1e; + short groundContactTime2 = 0x1f20; + byte groundImpact2 = 0x21; + short swingAngle2 = 0x2223; + byte foreFootLanding2 = 0x24; + byte midFootLanding2 = 0x25; + byte backFootLanding2 = 0x26; + byte eversionAngle2 = 0x27; + + ByteBuffer headerBuf = ByteBuffer.allocate(14); + headerBuf.putShort(workoutNumber); + headerBuf.putShort(dataNumber); + headerBuf.putInt(timestamp); + headerBuf.put(interval); + headerBuf.putShort(dataCount); + headerBuf.put(dataLength); + headerBuf.putShort(bitmap); + + ByteBuffer dataBuf = ByteBuffer.allocate(30); + + dataBuf.putShort(speed1); + dataBuf.putShort(cadence1); + dataBuf.putShort(stepLength1); + dataBuf.putShort(groundContactTime1); + dataBuf.put(groundImpact1); + dataBuf.putShort(swingAngle1); + dataBuf.put(foreFootLanding1); + dataBuf.put(midFootLanding1); + dataBuf.put(backFootLanding1); + dataBuf.put(eversionAngle1); + + dataBuf.putShort(speed2); + dataBuf.putShort(cadence2); + dataBuf.putShort(stepLength2); + dataBuf.putShort(groundContactTime2); + dataBuf.put(groundImpact2); + dataBuf.putShort(swingAngle2); + dataBuf.put(foreFootLanding2); + dataBuf.put(midFootLanding2); + dataBuf.put(backFootLanding2); + dataBuf.put(eversionAngle2); + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, workoutNumber) + .put(0x03, dataNumber) + .put(0x04, headerBuf.array()) + .put(0x05, dataBuf.array()) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x17, packet.serviceId); + Assert.assertEquals(0x0a, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof Workout.WorkoutData.Response); + + Assert.assertEquals(0x0102, ((Workout.WorkoutData.Response) packet).workoutNumber); + Assert.assertEquals(0x0304, ((Workout.WorkoutData.Response) packet).dataNumber); + Assert.assertArrayEquals(headerBuf.array(), ((Workout.WorkoutData.Response) packet).rawHeader); + Assert.assertArrayEquals(dataBuf.array(), ((Workout.WorkoutData.Response) packet).rawData); + + Assert.assertEquals(0x0102, ((Workout.WorkoutData.Response) packet).header.workoutNumber); + Assert.assertEquals(0x0304, ((Workout.WorkoutData.Response) packet).header.dataNumber); + Assert.assertEquals(0x05060708, ((Workout.WorkoutData.Response) packet).header.timestamp); + Assert.assertEquals(0x09, ((Workout.WorkoutData.Response) packet).header.interval); + Assert.assertEquals(0x0002, ((Workout.WorkoutData.Response) packet).header.dataCount); + Assert.assertEquals(0x0f, ((Workout.WorkoutData.Response) packet).header.dataLength); + Assert.assertEquals(0x0042, ((Workout.WorkoutData.Response) packet).header.bitmap); + + Assert.assertEquals(2, ((Workout.WorkoutData.Response) packet).dataList.size()); + + Assert.assertNull(((Workout.WorkoutData.Response) packet).dataList.get(0).unknownData); + Assert.assertEquals(0x0a0b, ((Workout.WorkoutData.Response) packet).dataList.get(0).speed); + Assert.assertEquals(0x0c0d, ((Workout.WorkoutData.Response) packet).dataList.get(0).cadence); + Assert.assertEquals(0x0e0f, ((Workout.WorkoutData.Response) packet).dataList.get(0).stepLength); + Assert.assertEquals(0x1011, ((Workout.WorkoutData.Response) packet).dataList.get(0).groundContactTime); + Assert.assertEquals(0x12, ((Workout.WorkoutData.Response) packet).dataList.get(0).impact); + Assert.assertEquals(0x1314, ((Workout.WorkoutData.Response) packet).dataList.get(0).swingAngle); + Assert.assertEquals(0x15, ((Workout.WorkoutData.Response) packet).dataList.get(0).foreFootLanding); + Assert.assertEquals(0x16, ((Workout.WorkoutData.Response) packet).dataList.get(0).midFootLanding); + Assert.assertEquals(0x17, ((Workout.WorkoutData.Response) packet).dataList.get(0).backFootLanding); + Assert.assertEquals(0x18, ((Workout.WorkoutData.Response) packet).dataList.get(0).eversionAngle); + + Assert.assertNull(((Workout.WorkoutData.Response) packet).dataList.get(1).unknownData); + Assert.assertEquals(0x191a, ((Workout.WorkoutData.Response) packet).dataList.get(1).speed); + Assert.assertEquals(0x1b1c, ((Workout.WorkoutData.Response) packet).dataList.get(1).cadence); + Assert.assertEquals(0x1d1e, ((Workout.WorkoutData.Response) packet).dataList.get(1).stepLength); + Assert.assertEquals(0x1f20, ((Workout.WorkoutData.Response) packet).dataList.get(1).groundContactTime); + Assert.assertEquals(0x21, ((Workout.WorkoutData.Response) packet).dataList.get(1).impact); + Assert.assertEquals(0x2223, ((Workout.WorkoutData.Response) packet).dataList.get(1).swingAngle); + Assert.assertEquals(0x24, ((Workout.WorkoutData.Response) packet).dataList.get(1).foreFootLanding); + Assert.assertEquals(0x25, ((Workout.WorkoutData.Response) packet).dataList.get(1).midFootLanding); + Assert.assertEquals(0x26, ((Workout.WorkoutData.Response) packet).dataList.get(1).backFootLanding); + Assert.assertEquals(0x27, ((Workout.WorkoutData.Response) packet).dataList.get(1).eversionAngle); + } + + @Test + public void testWorkoutPaceRequest() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.CryptoException { + short workoutNumber = 0x0102; + short paceNumber = 0x0304; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, workoutNumber) + .put(0x08, paceNumber) + ); + + byte[] expected = {(byte) 0x5a, (byte) 0x00, (byte) 0x2a, (byte) 0x00, (byte) 0x17, (byte) 0x0c, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x10, (byte) 0x0c, (byte) 0x18, (byte) 0x24, (byte) 0x67, (byte) 0x5e, (byte) 0xe9, (byte) 0x8d, (byte) 0x36, (byte) 0x5f, (byte) 0xde, (byte) 0x1c, (byte) 0x9e, (byte) 0xa0, (byte) 0xd7, (byte) 0x0a, (byte) 0x01, (byte) 0xd3, (byte) 0xce}; + + Workout.WorkoutPace.Request request = new Workout.WorkoutPace.Request(secretsProvider, workoutNumber, paceNumber); + + Assert.assertEquals(0x17, request.serviceId); + Assert.assertEquals(0x0c, request.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(request)); + Assert.assertTrue(request.complete); + List out = request.serialize(); + Assert.assertEquals(1, out.size()); + Assert.assertArrayEquals(expected, out.get(0)); + } + + @Test + public void testWorkoutPaceResponse() throws NoSuchFieldException, IllegalAccessException, HuaweiPacket.ParseException { + byte[] raw = {(byte) 0x5a, (byte) 0x00, (byte) 0x4a, (byte) 0x00, (byte) 0x17, (byte) 0x0c, (byte) 0x7c, (byte) 0x01, (byte) 0x01, (byte) 0x7d, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x7e, (byte) 0x30, (byte) 0xe8, (byte) 0xfe, (byte) 0xb9, (byte) 0x27, (byte) 0xa6, (byte) 0xc5, (byte) 0x81, (byte) 0x65, (byte) 0x51, (byte) 0xb8, (byte) 0x24, (byte) 0xfe, (byte) 0x2a, (byte) 0xdc, (byte) 0x3d, (byte) 0x22, (byte) 0xd7, (byte) 0x34, (byte) 0x62, (byte) 0xaf, (byte) 0x06, (byte) 0x5f, (byte) 0xfe, (byte) 0x9c, (byte) 0xe8, (byte) 0xa6, (byte) 0x87, (byte) 0x23, (byte) 0xd6, (byte) 0xc7, (byte) 0x7a, (byte) 0xeb, (byte) 0x07, (byte) 0x06, (byte) 0x5c, (byte) 0x35, (byte) 0xe8, (byte) 0x99, (byte) 0xd3, (byte) 0x96, (byte) 0x0b, (byte) 0x99, (byte) 0x38, (byte) 0x65, (byte) 0x48, (byte) 0xcf, (byte) 0x0f, (byte) 0x99, (byte) 0xe2, (byte) 0x23}; + + short workoutNumber = 0x0102; + short paceNumber = 0x0304; + + short distance1 = 0x0506; + byte type1 = 0x07; + int pace1 = 0x08090a0b; + + short distance2 = 0x0c0d; + byte type2 = 0x0e; + int pace2 = 0x0f101112; + short correction = 0x1314; + + Field tlvField = HuaweiPacket.class.getDeclaredField("tlv"); + tlvField.setAccessible(true); + + HuaweiTLV expectedTlv = new HuaweiTLV().put(0x81, new HuaweiTLV() + .put(0x02, workoutNumber) + .put(0x08, paceNumber) + .put(0x83, new HuaweiTLV() + .put(0x04, distance1) + .put(0x05, type1) + .put(0x06, pace1) + ) + .put(0x83, new HuaweiTLV() + .put(0x04, distance2) + .put(0x05, type2) + .put(0x06, pace2) + .put(0x09, correction) + ) + ); + + HuaweiPacket packet = new HuaweiPacket(secretsProvider).parse(raw); + packet.parseTlv(); + + Assert.assertEquals(0x17, packet.serviceId); + Assert.assertEquals(0x0c, packet.commandId); + Assert.assertEquals(expectedTlv, tlvField.get(packet)); + Assert.assertTrue(packet.complete); + Assert.assertTrue(packet instanceof Workout.WorkoutPace.Response); + Assert.assertEquals(0x0102, ((Workout.WorkoutPace.Response) packet).workoutNumber); + Assert.assertEquals(0x0304, ((Workout.WorkoutPace.Response) packet).paceNumber); + Assert.assertEquals(2, ((Workout.WorkoutPace.Response) packet).blocks.size()); + + Assert.assertEquals(0x0506, ((Workout.WorkoutPace.Response) packet).blocks.get(0).distance); + Assert.assertEquals(0x07, ((Workout.WorkoutPace.Response) packet).blocks.get(0).type); + Assert.assertEquals(0x08090a0b, ((Workout.WorkoutPace.Response) packet).blocks.get(0).pace); + Assert.assertEquals(0, ((Workout.WorkoutPace.Response) packet).blocks.get(0).correction); + + Assert.assertEquals(0x0c0d, ((Workout.WorkoutPace.Response) packet).blocks.get(1).distance); + Assert.assertEquals(0x0e, ((Workout.WorkoutPace.Response) packet).blocks.get(1).type); + Assert.assertEquals(0x0f101112, ((Workout.WorkoutPace.Response) packet).blocks.get(1).pace); + Assert.assertEquals(0x1314, ((Workout.WorkoutPace.Response) packet).blocks.get(1).correction); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceTypeTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceTypeTest.java index a3ad7c8dc..04aa25233 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceTypeTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceTypeTest.java @@ -19,28 +19,10 @@ package nodomain.freeyourgadget.gadgetbridge.model; import org.junit.Assert; import org.junit.Test; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.test.TestBase; public class DeviceTypeTest extends TestBase { - @Test - public void ensureNoDuplicateKeys() { - final Set knownKeys = new HashSet<>(); - - final List duplicateKeys = Arrays.stream(DeviceType.values()) - .map(DeviceType::getKey) - .filter(k -> !knownKeys.add(k)) - .collect(Collectors.toList()); - - Assert.assertTrue("There are duplicated device keys: " + duplicateKeys, duplicateKeys.isEmpty()); - } - @Test public void ensureNoMissingDeviceInfo() { // Check that all coordinators for all device types declare valid device names, icons and manufacturer diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/cmfwatchpro/CmfCommandTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/cmfwatchpro/CmfCommandTest.java new file mode 100644 index 000000000..678be855e --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/cmfwatchpro/CmfCommandTest.java @@ -0,0 +1,39 @@ +/* Copyright (C) 2024 JosÊ Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.cmfwatchpro; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class CmfCommandTest { + @Test + public void commandEnumCheckNoOverlap() { + // Ensure that no 2 commands overlap in codes + final Map knownCodes = new HashMap<>(); + for (final CmfCommand cmd : CmfCommand.values()) { + final Boolean existingCode = knownCodes.put( + String.format("cmd1=0x%04x cmd2=0x%04x", cmd.getCmd1(), cmd.getCmd2()), + true + ); + assertNull("Commands with overlapping codes", existingCode); + } + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/Huami2021SupportTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/ZeppOsSupportTest.java similarity index 93% rename from app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/Huami2021SupportTest.java rename to app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/ZeppOsSupportTest.java index 4850c4db5..ae64b1916 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/Huami2021SupportTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/ZeppOsSupportTest.java @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.huami; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppos; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Context; @@ -35,10 +35,10 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WriteAction; -public class Huami2021SupportTest { +public class ZeppOsSupportTest { @Test public void testSetCurrentTimeWithService() { - final Huami2021Support support = createSupport(); + final ZeppOsSupport support = createSupport(); final TransactionBuilder testTransactionBuilder = new TransactionBuilder("test"); support.setCurrentTimeWithService(testTransactionBuilder); @@ -47,8 +47,8 @@ public class Huami2021SupportTest { Assert.assertArrayEquals(new byte[]{-26, 7, 12, 15, 20, 38, 53, 4, 0, 8, 4}, action.getValue()); } - private Huami2021Support createSupport() { - return new Huami2021Support() { + private ZeppOsSupport createSupport() { + return new ZeppOsSupport() { @Override public BluetoothGattCharacteristic getCharacteristic(final UUID uuid) { return new BluetoothGattCharacteristic(null, 0, 0); diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/TestDebugRequestParser.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/TestDebugRequestParser.java new file mode 100644 index 000000000..1b20d1f89 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/TestDebugRequestParser.java @@ -0,0 +1,396 @@ +/* Copyright (C) 2023 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Context; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btbr.Transaction; +import nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.DebugRequest; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request; + +public class TestDebugRequestParser { + + HuaweiSupportProvider supportProvider = new HuaweiSupportProvider(new HuaweiLESupport()) { + + @Override + public boolean isBLE() { + return true; + } + + @Override + public Context getContext() { + return null; + } + + @Override + public GBDevice getDevice() { + return null; + } + + @Override + public byte[] getSerial() { + return new byte[0]; + } + + @Override + public String getDeviceMac() { + return null; + } + + @Override + public byte[] getMacAddress() { + return new byte[0]; + } + + @Override + public byte[] getAndroidId() { + return new byte[0]; + } + + @Override + public short getNotificationId() { + return 0; + } + + @Override + public TransactionBuilder createBrTransactionBuilder(String taskName) { + return null; + } + + @Override + public nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder createLeTransactionBuilder(String taskName) { + return null; + } + + @Override + public void performConnected(Transaction transaction) throws IOException { + + } + + @Override + public void performConnected(nodomain.freeyourgadget.gadgetbridge.service.btle.Transaction transaction) throws IOException { + + } + + @Override + public void evaluateGBDeviceEvent(GBDeviceEvent deviceEvent) { + + } + + @Override + public BluetoothGattCharacteristic getLeCharacteristic(UUID uuid) { + return null; + } + + @Override + public HuaweiPacket.ParamsProvider getParamsProvider() { + return null; + } + + @Override + public void addInProgressRequest(Request request) { + + } + + @Override + public void removeInProgressRequests(Request request) { + + } + + @Override + public void setSecretKey(byte[] authKey) { + + } + + @Override + public byte[] getSecretKey() { + return new byte[0]; + } + + @Override + public void addTotalFitnessData(int steps, int calories, int distance) { + + } + + @Override + public void addSleepActivity(int timestamp, short duration, byte type) { + + } + + @Override + public void addStepData(int timestamp, short steps, short calories, short distance, byte spo, byte heartrate) { + + } + + @Override + public Long addWorkoutTotalsData(Workout.WorkoutTotals.Response packet) { + return null; + } + + @Override + public void addWorkoutSampleData(Long workoutId, List dataList) { + + } + + @Override + public void addWorkoutPaceData(Long workoutId, List paceList) { + + } + + @Override + public void sendSetMusic() { + + } + }; + + @Test + public void emptyPacket() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void emptyTag() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1)); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(1,/)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void byteTag() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1, (byte) 1)); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(1,B1)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void shortTag() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1, (short) 1)); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(1,S1)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void integerTag() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1, (int) 1)); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(1,I1)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void booleanTag() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1, true)); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(1,b1)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void arrayTag() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1, new byte[] {(byte) 0xCA, (byte) 0xFE})); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(1,aCAFE)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void stringTag() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1, new byte[] {0x79, 0x65, 0x73})); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(1,-yes)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void hexValues() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV().put(1, new byte[] {0x79, 0x65, 0x73})); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("0x01,0x1,false,(0x01,-yes)"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void largeServiceCommand() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = (byte) 0xff; + expected.commandId = (byte) 255; + expected.setEncryption(false); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("0xff,255,false"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void subTlv() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV() + .put(129, new HuaweiTLV() + .put(1) + .put(2) + )); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(129,(1,/),(2,/))"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void subSubSubTlv() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV() + .put(129, new HuaweiTLV() + .put(129, new HuaweiTLV() + .put(129, new HuaweiTLV() + .put(1) + ) + ) + )); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(129,(129,(129,(1,/))))"); + + Assert.assertEquals(expected, packet); + } + + @Test + public void subTlvVCombined() throws Request.RequestCreationException { + DebugRequest debugRequest = new DebugRequest(supportProvider); + + HuaweiPacket expected = new HuaweiPacket(supportProvider.getParamsProvider()); + expected.serviceId = 1; + expected.commandId = 1; + expected.setEncryption(false); + expected.setTlv(new HuaweiTLV() + .put(129, new HuaweiTLV() + .put(1) + .put(2, true) + ) + .put(1, true) + ); + expected.complete = true; + + HuaweiPacket packet = debugRequest.parseDebugString("1,1,false,(129,(1,/),(2,b1)),(1,b1)"); + + Assert.assertEquals(expected, packet); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/TestResponseManager.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/TestResponseManager.java new file mode 100644 index 000000000..dcc210b60 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/TestResponseManager.java @@ -0,0 +1,451 @@ +/* Copyright (C) 2022-2023 Martin.JM + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Context; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket; +import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btbr.Transaction; +import nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.Request; + +@RunWith(MockitoJUnitRunner.class) +public class TestResponseManager { + + HuaweiSupportProvider supportProvider = new HuaweiSupportProvider(new HuaweiLESupport()) { + + @Override + public boolean isBLE() { + return true; + } + + @Override + public Context getContext() { + return null; + } + + @Override + public GBDevice getDevice() { + return null; + } + + @Override + public byte[] getSerial() { + return new byte[0]; + } + + @Override + public String getDeviceMac() { + return null; + } + + @Override + public byte[] getMacAddress() { + return new byte[0]; + } + + @Override + public byte[] getAndroidId() { + return new byte[0]; + } + + @Override + public short getNotificationId() { + return 0; + } + + @Override + public TransactionBuilder createBrTransactionBuilder(String taskName) { + return null; + } + + @Override + public nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder createLeTransactionBuilder(String taskName) { + return null; + } + + @Override + public void performConnected(Transaction transaction) throws IOException { + + } + + @Override + public void performConnected(nodomain.freeyourgadget.gadgetbridge.service.btle.Transaction transaction) throws IOException { + + } + + @Override + public void evaluateGBDeviceEvent(GBDeviceEvent deviceEvent) { + + } + + @Override + public BluetoothGattCharacteristic getLeCharacteristic(UUID uuid) { + return null; + } + + @Override + public HuaweiPacket.ParamsProvider getParamsProvider() { + return null; + } + + @Override + public void addInProgressRequest(Request request) { + + } + + @Override + public void removeInProgressRequests(Request request) { + + } + + @Override + public void setSecretKey(byte[] authKey) { + + } + + @Override + public byte[] getSecretKey() { + return new byte[0]; + } + + @Override + public void addTotalFitnessData(int steps, int calories, int distance) { + + } + + @Override + public void addSleepActivity(int timestamp, short duration, byte type) { + + } + + @Override + public void addStepData(int timestamp, short steps, short calories, short distance, byte spo, byte heartrate) { + + } + + @Override + public Long addWorkoutTotalsData(Workout.WorkoutTotals.Response packet) { + return null; + } + + @Override + public void addWorkoutSampleData(Long workoutId, List dataList) { + + } + + @Override + public void addWorkoutPaceData(Long workoutId, List paceList) { + + } + + @Override + public void sendSetMusic() { + + } + }; + + Field handlersField; + Field receivedPacketField; + Field asynchronousResponseField; + + @Before + public void beforeClass() throws NoSuchFieldException { + handlersField = ResponseManager.class.getDeclaredField("handlers"); + handlersField.setAccessible(true); + + asynchronousResponseField = ResponseManager.class.getDeclaredField("asynchronousResponse"); + asynchronousResponseField.setAccessible(true); + + receivedPacketField = ResponseManager.class.getDeclaredField("receivedPacket"); + receivedPacketField.setAccessible(true); + } + + @Test + public void testAddHandler() throws IllegalAccessException { + Request input = new Request(supportProvider); + + List expectedHandlers = Collections.synchronizedList(new ArrayList()); + expectedHandlers.add(input); + + ResponseManager responseManager = new ResponseManager(supportProvider); + responseManager.addHandler(input); + + Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); + } + + @Test + public void testRemoveHandler() throws IllegalAccessException { + Request input = new Request(supportProvider); + Request extra = new Request(supportProvider); + + List inputHandlers = Collections.synchronizedList(new ArrayList()); + inputHandlers.add(extra); + inputHandlers.add(input); + inputHandlers.add(extra); + + List expectedHandlers = Collections.synchronizedList(new ArrayList()); + expectedHandlers.add(extra); + expectedHandlers.add(extra); + + ResponseManager responseManager = new ResponseManager(supportProvider); + handlersField.set(responseManager, inputHandlers); + + responseManager.removeHandler(input); + + Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); + } + + @Test + public void testHandleDataCompletePacketSynchronous() throws Exception { + // Note that this is not a proper packet, but that doesn't matter as we're not testing + // the packet parsing. + byte[] input = {0x01, 0x02, 0x03, 0x04}; + + AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); + + HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); + mockHuaweiPacket.complete = true; + when(mockHuaweiPacket.parse((byte[]) any())) + .thenReturn(mockHuaweiPacket); + + Request request1 = Mockito.mock(Request.class); + when(request1.handleResponse((HuaweiPacket) any())) + .thenReturn(true); + Request request2 = Mockito.mock(Request.class); + // FIXME: Removed due to UnnecessaryStubbingException after mockito-core update + //when(request2.handleResponse((HuaweiPacket) any())) + // .thenReturn(false); + + List inputHandlers = Collections.synchronizedList(new ArrayList()); + inputHandlers.add(request1); + inputHandlers.add(request2); + + List expectedHandlers = Collections.synchronizedList(new ArrayList()); + expectedHandlers.add(request2); + + ResponseManager responseManager = new ResponseManager(supportProvider); + handlersField.set(responseManager, inputHandlers); + receivedPacketField.set(responseManager, mockHuaweiPacket); + asynchronousResponseField.set(responseManager, mockAsynchronousResponse); + + responseManager.handleData(input); + + Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); + Assert.assertNull(receivedPacketField.get(responseManager)); + + verify(mockHuaweiPacket, times(1)).parse(input); + verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); + verify(request1, times(1)).handleResponse(mockHuaweiPacket); + verify(request1, times(1)).handleResponse(); + verify(request2, times(0)).handleResponse((HuaweiPacket) any()); + verify(request2, times(0)).handleResponse(); + } + + @Test + public void testHandleDataCompletePacketAsynchronous() throws Exception { + // Note that this is not a proper packet, but that doesn't matter as we're not testing + // the packet parsing. + byte[] input = {0x01, 0x02, 0x03, 0x04}; + + AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); + + HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); + mockHuaweiPacket.complete = true; + when(mockHuaweiPacket.parse((byte[]) any())) + .thenReturn(mockHuaweiPacket); + + Request request1 = Mockito.mock(Request.class); + when(request1.handleResponse((HuaweiPacket) any())) + .thenReturn(false); + Request request2 = Mockito.mock(Request.class); + when(request2.handleResponse((HuaweiPacket) any())) + .thenReturn(false); + + List inputHandlers = Collections.synchronizedList(new ArrayList()); + inputHandlers.add(request1); + inputHandlers.add(request2); + + List expectedHandlers = Collections.synchronizedList(new ArrayList()); + expectedHandlers.add(request1); + expectedHandlers.add(request2); + + ResponseManager responseManager = new ResponseManager(supportProvider); + handlersField.set(responseManager, inputHandlers); + receivedPacketField.set(responseManager, mockHuaweiPacket); + asynchronousResponseField.set(responseManager, mockAsynchronousResponse); + + responseManager.handleData(input); + + Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); + Assert.assertNull(receivedPacketField.get(responseManager)); + + verify(mockHuaweiPacket, times(1)).parse(input); + verify(mockAsynchronousResponse, times(1)).handleResponse(mockHuaweiPacket); + verify(request1, times(1)).handleResponse(mockHuaweiPacket); + verify(request1, times(0)).handleResponse(); + verify(request2, times(1)).handleResponse(mockHuaweiPacket); + verify(request2, times(0)).handleResponse(); + } + + @Test + public void testHandleDataTwoPartialPacketsSynchronous() throws Exception { + // Note that this is not a proper packet, but that doesn't matter as we're not testing + // the packet parsing. + byte[] input1 = {0x01, 0x02, 0x03, 0x04}; + byte[] input2 = {0x05, 0x06, 0x07, 0x08}; + + AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); + + HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); + mockHuaweiPacket.complete = false; + when(mockHuaweiPacket.parse((byte[]) any())) + .thenReturn(mockHuaweiPacket); + + Request request1 = Mockito.mock(Request.class); + when(request1.handleResponse((HuaweiPacket) any())) + .thenReturn(true); + Request request2 = Mockito.mock(Request.class); + // FIXME: Removed due to UnnecessaryStubbingException after mockito-core update + //when(request2.handleResponse((HuaweiPacket) any())) + // .thenReturn(false); + + List inputHandlers = Collections.synchronizedList(new ArrayList()); + inputHandlers.add(request1); + inputHandlers.add(request2); + + List expectedHandlers1 = Collections.synchronizedList(new ArrayList()); + expectedHandlers1.add(request1); + expectedHandlers1.add(request2); + + List expectedHandlers2 = Collections.synchronizedList(new ArrayList()); + expectedHandlers2.add(request2); + + ResponseManager responseManager = new ResponseManager(supportProvider); + handlersField.set(responseManager, inputHandlers); + receivedPacketField.set(responseManager, mockHuaweiPacket); + asynchronousResponseField.set(responseManager, mockAsynchronousResponse); + + responseManager.handleData(input1); + + Assert.assertEquals(expectedHandlers1, handlersField.get(responseManager)); + Assert.assertEquals(mockHuaweiPacket, receivedPacketField.get(responseManager)); + + verify(mockHuaweiPacket, times(1)).parse(input1); + verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); + verify(request1, times(0)).handleResponse(mockHuaweiPacket); + verify(request1, times(0)).handleResponse(); + verify(request2, times(0)).handleResponse((HuaweiPacket) any()); + verify(request2, times(0)).handleResponse(); + + mockHuaweiPacket.complete = true; + responseManager.handleData(input2); + + Assert.assertEquals(expectedHandlers2, handlersField.get(responseManager)); + Assert.assertNull(receivedPacketField.get(responseManager)); + + verify(mockHuaweiPacket, times(1)).parse(input2); + verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); + verify(request1, times(1)).handleResponse(mockHuaweiPacket); + verify(request1, times(1)).handleResponse(); + verify(request2, times(0)).handleResponse((HuaweiPacket) any()); + verify(request2, times(0)).handleResponse(); + } + + @Test + public void testHandleDataTwoPartialPacketsAsynchronous() throws Exception { + // Note that this is not a proper packet, but that doesn't matter as we're not testing + // the packet parsing. + byte[] input1 = {0x01, 0x02, 0x03, 0x04}; + byte[] input2 = {0x05, 0x06, 0x07, 0x08}; + + AsynchronousResponse mockAsynchronousResponse = Mockito.mock(AsynchronousResponse.class); + + HuaweiPacket mockHuaweiPacket = Mockito.mock(HuaweiPacket.class); + mockHuaweiPacket.complete = false; + when(mockHuaweiPacket.parse((byte[]) any())) + .thenReturn(mockHuaweiPacket); + + Request request1 = Mockito.mock(Request.class); + when(request1.handleResponse((HuaweiPacket) any())) + .thenReturn(false); + Request request2 = Mockito.mock(Request.class); + when(request2.handleResponse((HuaweiPacket) any())) + .thenReturn(false); + + List inputHandlers = Collections.synchronizedList(new ArrayList()); + inputHandlers.add(request1); + inputHandlers.add(request2); + + List expectedHandlers = Collections.synchronizedList(new ArrayList()); + expectedHandlers.add(request1); + expectedHandlers.add(request2); + + ResponseManager responseManager = new ResponseManager(supportProvider); + handlersField.set(responseManager, inputHandlers); + receivedPacketField.set(responseManager, mockHuaweiPacket); + asynchronousResponseField.set(responseManager, mockAsynchronousResponse); + + responseManager.handleData(input1); + + Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); + Assert.assertEquals(mockHuaweiPacket, receivedPacketField.get(responseManager)); + + verify(mockHuaweiPacket, times(1)).parse(input1); + verify(mockAsynchronousResponse, times(0)).handleResponse((HuaweiPacket) any()); + verify(request1, times(0)).handleResponse(mockHuaweiPacket); + verify(request1, times(0)).handleResponse(); + verify(request2, times(0)).handleResponse((HuaweiPacket) any()); + verify(request2, times(0)).handleResponse(); + + mockHuaweiPacket.complete = true; + responseManager.handleData(input2); + + Assert.assertEquals(expectedHandlers, handlersField.get(responseManager)); + Assert.assertNull(receivedPacketField.get(responseManager)); + + verify(mockHuaweiPacket, times(1)).parse(input2); + verify(mockAsynchronousResponse, times(1)).handleResponse((HuaweiPacket) any()); + verify(request1, times(1)).handleResponse(mockHuaweiPacket); + verify(request1, times(0)).handleResponse(); + verify(request2, times(1)).handleResponse((HuaweiPacket) any()); + verify(request2, times(0)).handleResponse(); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/XiaomiActivityFileIdTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/XiaomiActivityFileIdTest.java new file mode 100644 index 000000000..f214d0f10 --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/activity/XiaomiActivityFileIdTest.java @@ -0,0 +1,83 @@ +/* Copyright (C) 2023 JosÊ Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.activity; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class XiaomiActivityFileIdTest { + @Test + public void testEncode() { + final byte[] expectedEncoding = GB.hexStringToByteArray("21F3286504008C"); + final XiaomiActivityFileId xiaomiActivityFileId = new XiaomiActivityFileId( + new Date(1697182497000L), + 4, + 3, + 1, + 8, + 0 + ); + + assertArrayEquals(expectedEncoding, xiaomiActivityFileId.toBytes()); + } + + @Test + public void testDecode() { + final byte[] bytes = GB.hexStringToByteArray("21F328650403A0"); + final XiaomiActivityFileId expectedFileId = XiaomiActivityFileId.from(bytes); + + assertEquals(1697182497000L, expectedFileId.getTimestamp().getTime()); + assertEquals(4, expectedFileId.getTimezone()); + assertEquals(3, expectedFileId.getVersion()); + assertEquals(XiaomiActivityFileId.Type.SPORTS, expectedFileId.getType()); + assertEquals(XiaomiActivityFileId.Subtype.SPORTS_FREESTYLE, expectedFileId.getSubtype()); + assertEquals(XiaomiActivityFileId.DetailType.DETAILS, expectedFileId.getDetailType()); + } + + @Test + public void testDecodeEncode() { + final byte[] bytes = GB.hexStringToByteArray("21F328650403A021F3286504008C"); + + final ByteBuffer bufDecode = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); + + final List fileIds = new ArrayList<>(); + + while (bufDecode.position() < bufDecode.limit()) { + final XiaomiActivityFileId fileId = XiaomiActivityFileId.from(bufDecode); + fileIds.add(fileId); + System.out.println(fileId); + } + + final ByteBuffer bufEncode = ByteBuffer.allocate(fileIds.size() * 7).order(ByteOrder.LITTLE_ENDIAN); + + for (final XiaomiActivityFileId fileId : fileIds) { + bufEncode.put(fileId.toBytes()); + } + + assertArrayEquals(bytes, bufEncode.array()); + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/ZipFileTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBZipFileTest.java similarity index 93% rename from app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/ZipFileTest.java rename to app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBZipFileTest.java index e9934b135..d634643d6 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/ZipFileTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBZipFileTest.java @@ -12,10 +12,10 @@ import java.nio.charset.StandardCharsets; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import nodomain.freeyourgadget.gadgetbridge.util.ZipFile; +import nodomain.freeyourgadget.gadgetbridge.util.GBZipFile; import nodomain.freeyourgadget.gadgetbridge.util.ZipFileException; -public class ZipFileTest extends TestBase { +public class GBZipFileTest extends TestBase { private static final String TEST_FILE_NAME = "manifest.json"; private static final String TEST_NESTED_FILE_NAME = "directory/manifest.json"; private static final String TEST_FILE_CONTENTS_1 = "{ \"mykey\": \"myvalue\", \"myarr\": [0, 1, 2, 3] }"; @@ -44,7 +44,7 @@ public class ZipFileTest extends TestBase { byte[] zipArchive = createZipArchive(TEST_FILE_NAME, contents); - ZipFile zipFile = new ZipFile(zipArchive); + GBZipFile zipFile = new GBZipFile(zipArchive); String readContents = new String(zipFile.getFileFromZip(TEST_FILE_NAME)); Assert.assertEquals(contents, readContents); @@ -56,7 +56,7 @@ public class ZipFileTest extends TestBase { byte[] zipArchive = createZipArchive(TEST_FILE_NAME, contents); - ZipFile zipFile = new ZipFile(zipArchive); + GBZipFile zipFile = new GBZipFile(zipArchive); String readContents = new String(zipFile.getFileFromZip(TEST_FILE_NAME)); Assert.assertEquals(contents, readContents); @@ -68,7 +68,7 @@ public class ZipFileTest extends TestBase { byte[] zipArchive = createZipArchive(TEST_FILE_NAME, contents); - ZipFile zipFile = new ZipFile(zipArchive); + GBZipFile zipFile = new GBZipFile(zipArchive); String readContents = new String(zipFile.getFileFromZip(TEST_FILE_NAME)); Assert.assertEquals(contents, readContents); @@ -80,7 +80,7 @@ public class ZipFileTest extends TestBase { byte[] zipArchive = createZipArchive(TEST_FILE_NAME, contents); - ZipFile zipFile = new ZipFile(zipArchive); + GBZipFile zipFile = new GBZipFile(zipArchive); String readContents = new String(zipFile.getFileFromZip(TEST_FILE_NAME)); Assert.assertEquals(contents, readContents); @@ -92,7 +92,7 @@ public class ZipFileTest extends TestBase { byte[] zipArchive = createZipArchive(TEST_NESTED_FILE_NAME, contents); - ZipFile zipFile = new ZipFile(zipArchive); + GBZipFile zipFile = new GBZipFile(zipArchive); String readContents = new String(zipFile.getFileFromZip(TEST_NESTED_FILE_NAME)); Assert.assertEquals(contents, readContents); @@ -112,7 +112,7 @@ public class ZipFileTest extends TestBase { writeFileToZip(contents3, "file3", zipWriteStream); zipWriteStream.close(); - ZipFile zipFile = new ZipFile(baos.toByteArray()); + GBZipFile zipFile = new GBZipFile(baos.toByteArray()); String readContents2 = new String(zipFile.getFileFromZip("file2")); String readContents1 = new String(zipFile.getFileFromZip("file1")); String readContents3 = new String(zipFile.getFileFromZip("file3")); @@ -132,7 +132,7 @@ public class ZipFileTest extends TestBase { writeFileToZip("Hello, World!", "folder1/file3", zipWriteStream); zipWriteStream.close(); - final ZipFile zipFile = new ZipFile(baos.toByteArray()); + final GBZipFile zipFile = new GBZipFile(baos.toByteArray()); Assert.assertTrue(zipFile.fileExists("file2")); Assert.assertTrue(zipFile.fileExists("file1")); Assert.assertTrue(zipFile.fileExists("folder1/file3")); diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/util/language/LanguageUtilsTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/util/language/LanguageUtilsTest.java index 0f25a5244..08a359860 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/util/language/LanguageUtilsTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/util/language/LanguageUtilsTest.java @@ -4,9 +4,13 @@ import android.content.SharedPreferences; import org.junit.Test; +import java.util.Arrays; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.test.TestBase; +import nodomain.freeyourgadget.gadgetbridge.util.language.impl.CzechTransliterator; +import nodomain.freeyourgadget.gadgetbridge.util.language.impl.ExtendedAsciiTransliterator; import nodomain.freeyourgadget.gadgetbridge.util.language.impl.FlattenToAsciiTransliterator; import static org.junit.Assert.assertEquals; @@ -96,9 +100,9 @@ public class LanguageUtilsTest extends TestBase { final Transliterator transliterator = LanguageUtils.getTransliterator("bengali"); // input with cyrillic and diacritic letters - String[] inputs = { "āĻ…āĻ¨āĻŋāĻ°ā§āĻĻā§āĻ§", "āĻŦāĻŋāĻœā§āĻžāĻžāĻ¨āĻ¯āĻžāĻ¤ā§āĻ°āĻž āĻšāĻ˛āĻ›ā§‡ āĻšāĻ˛āĻŦā§‡āĨ¤", "āĻ†āĻŽāĻŋ āĻ¸āĻŦ āĻĻā§‡āĻ–ā§‡āĻļā§āĻ¨ā§‡ āĻ•ā§āĻˇā§‡āĻĒā§‡ āĻ—āĻŋā§Ÿā§‡ āĻ•āĻ°āĻŋ āĻŦāĻžāĻ™āĻ˛āĻžā§Ÿ āĻšāĻŋā§ŽāĻ•āĻžāĻ°!", - "āĻ†āĻŽāĻžāĻ° āĻœāĻžāĻ­āĻž āĻ•ā§‹āĻĄ is so bad! āĻ•ā§€ āĻ†āĻ° āĻŦāĻ˛āĻŦā§‹!" }; - String[] outputs = { "oniruddho", "biggaanJaatraa cholchhe cholbe.", + String[] inputs = {"āĻ…āĻ¨āĻŋāĻ°ā§āĻĻā§āĻ§", "āĻŦāĻŋāĻœā§āĻžāĻžāĻ¨āĻ¯āĻžāĻ¤ā§āĻ°āĻž āĻšāĻ˛āĻ›ā§‡ āĻšāĻ˛āĻŦā§‡āĨ¤", "āĻ†āĻŽāĻŋ āĻ¸āĻŦ āĻĻā§‡āĻ–ā§‡āĻļā§āĻ¨ā§‡ āĻ•ā§āĻˇā§‡āĻĒā§‡ āĻ—āĻŋā§Ÿā§‡ āĻ•āĻ°āĻŋ āĻŦāĻžāĻ™āĻ˛āĻžā§Ÿ āĻšāĻŋā§ŽāĻ•āĻžāĻ°!", + "āĻ†āĻŽāĻžāĻ° āĻœāĻžāĻ­āĻž āĻ•ā§‹āĻĄ is so bad! āĻ•ā§€ āĻ†āĻ° āĻŦāĻ˛āĻŦā§‹!"}; + String[] outputs = {"oniruddho", "biggaanJaatraa cholchhe cholbe.", "aami sob dekheshune kkhepe giye kori baanglaay chitkaar!", "aamaar jaabhaa koD is so bad! kii aar bolbo!"}; @@ -189,6 +193,16 @@ public class LanguageUtilsTest extends TestBase { assertEquals("georgian transliteration failed", expected, output); } + @Test + public void testStringTransliterateHungarian() { + final Transliterator transliterator = LanguageUtils.getTransliterator("hungarian"); + + String input = "ÃĄ Ê í Ãŗ Ãļ ő Ãŧ Åą"; + String output = transliterator.transliterate(input); + String expected = "a e i o o o u u"; + assertEquals("hungarian transliteration failed", expected, output); + } + @Test public void testStringTransliterateCommonSymbols() { final Transliterator transliterator = LanguageUtils.getTransliterator("common_symbols"); @@ -217,12 +231,23 @@ public class LanguageUtilsTest extends TestBase { @Test public void testFlattenToAscii() throws Exception { final FlattenToAsciiTransliterator transliterator = new FlattenToAsciiTransliterator(); - String input = "ä ș ț ă"; + String input = "ä ș ț ă īŦne"; String output = transliterator.transliterate(input); - String expected = "a s t a"; + String expected = "a s t a fine"; assertEquals("flatten to ascii transliteration failed", expected, output); } + @Test + public void testMultitransliterator() throws Exception { + final MultiTransliterator multiTransliterator = new MultiTransliterator(Arrays.asList( + new CzechTransliterator(), + new ExtendedAsciiTransliterator(), + new FlattenToAsciiTransliterator() + )); + assertEquals("Zlutoucky kun upel \"dabelske\" \"ody\"", multiTransliterator.transliterate("ÅŊluÅĨoučkÃŊ kůň Ãēpěl ÂģÄÃĄbelskÊÂĢ „Ãŗdy“")); + assertEquals("300 Kc", multiTransliterator.transliterate("300\u00A0Kč")); + } + @Test public void testTransliterateOption() throws Exception { enableTransliteration(false); diff --git a/build.gradle b/build.gradle index e09271d1c..ed86e9363 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' + classpath 'com.android.tools.build:gradle:7.4.2' classpath 'gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:2.0.0' classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.17' diff --git a/external/build_fossil_hr_watchface.sh b/external/build_fossil_hr_gbapps.sh similarity index 68% rename from external/build_fossil_hr_watchface.sh rename to external/build_fossil_hr_gbapps.sh index 28a8acf3d..82a341c4e 100755 --- a/external/build_fossil_hr_watchface.sh +++ b/external/build_fossil_hr_gbapps.sh @@ -4,8 +4,8 @@ gcc_version="$(gcc -v 2>&1 | grep -oe '^gcc version [0-9][0-9\.]*[0-9]' | sed 's (( gcc_version > 11 )) && git apply ../patches/jerryscript-gcc-12-build-fix.patch python3 tools/build.py --jerry-cmdline-snapshot ON popd -pushd fossil-hr-watchface -export jerry=../jerryscript/build/bin/jerry-snapshot +pushd fossil-hr-gbapps/watchface +export jerry=../../jerryscript/build/bin/jerry-snapshot $jerry generate -f '' open_source_watchface.js -o openSourceWatchface.bin $jerry generate -f '' widget_date.js -o widgetDate.bin $jerry generate -f '' widget_weather.js -o widgetWeather.bin @@ -19,4 +19,10 @@ $jerry generate -f '' widget_chanceofrain.js -o widgetChanceOfRain.bin $jerry generate -f '' widget_uv.js -o widgetUV.bin $jerry generate -f '' widget_custom.js -o widgetCustom.bin popd -mv fossil-hr-watchface/*.bin ../app/src/main/assets/fossil_hr/ +mv fossil-hr-gbapps/watchface/*.bin ../app/src/main/assets/fossil_hr/ +pushd fossil-hr-gbapps/navigationApp +mkdir -p build/files/{code,config,display_name,icons,layout} +$jerry generate -f '' app.js -o build/files/code/navigationApp +python3 ../../pack.py -i build/ -o navigationApp.wapp +popd +mv fossil-hr-gbapps/navigationApp/navigationApp.wapp ../app/src/main/assets/fossil_hr/ diff --git a/external/fossil-hr-gbapps b/external/fossil-hr-gbapps new file mode 160000 index 000000000..3c9007742 --- /dev/null +++ b/external/fossil-hr-gbapps @@ -0,0 +1 @@ +Subproject commit 3c900774207d9dd904886433d672d22d5bd0dea4 diff --git a/external/fossil-hr-watchface b/external/fossil-hr-watchface deleted file mode 160000 index 24247ae23..000000000 --- a/external/fossil-hr-watchface +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 24247ae23e1b903ddcc1e4e9cfb4ad7280a77db2 diff --git a/external/pack.py b/external/pack.py new file mode 100644 index 000000000..4326724da --- /dev/null +++ b/external/pack.py @@ -0,0 +1,124 @@ +# File downloaded from https://github.com/dakhnod/Fossil-HR-SDK/ + +import sys +import os +import json +import crc32c +import getopt + +class Packer: + def __init__(self): + self.file_block = bytearray() + + def put_int(self, content, length=4): + self.file_block.extend(content.to_bytes(length, 'little')) + + def pack(self, input_dir_path, output_file_path): + start_path = os.getcwd() + + if not os.path.isdir(input_dir_path): + print('cannot find dir %s' % input_dir_path) + exit() + os.chdir(input_dir_path) + + with open('app.json', 'r') as json_file: + app_meta = json.load(json_file) + + os.chdir('files') + + all_files = [] + dir_sizes = {} + + for files_dir_list in [('code', False), ('icons', False), ('layout', True), ('display_name', True), ('config', True)]: + dir_size = 0 + files_dir = files_dir_list[0] + append_null = files_dir_list[1] + files = os.listdir(files_dir) + os.chdir(files_dir) + for file in sorted(files): + print(f'packing {file}') + with open(file, 'rb')as f: + contents = bytearray(f.read()) + if append_null: + contents.append(0) + file_size = contents.__len__() + all_files.append({ + 'filename': file, + 'contents': contents, + 'size': file_size + }) + dir_size = dir_size + file_size + file.__len__() + 4 # null byte + size bytes + os.chdir(os.pardir) + dir_sizes[files_dir] = dir_size + + offset_code = 88 + offset_icons = offset_code + dir_sizes['code'] + offset_layout = offset_icons + dir_sizes['icons'] + offset_display_name = offset_layout + dir_sizes['layout'] + offset_config = offset_display_name + dir_sizes['display_name'] + offset_file_end = offset_config + dir_sizes['config'] + + self.file_block.extend([int(octet) for octet in app_meta['version'].split('.')]) + + self.put_int(0) + self.put_int(0) + self.put_int(offset_code) + self.put_int(offset_icons) + self.put_int(offset_layout) + self.put_int(offset_display_name) + self.put_int(offset_display_name) + self.put_int(offset_config) + self.put_int(offset_file_end) + self.put_int(0) + self.put_int(0) + self.put_int(0) + self.put_int(0) + self.put_int(0) + self.put_int(0) + self.put_int(0) + self.put_int(0) + self.put_int(0) + + for file in all_files: + filename = file['filename'] + self.put_int(filename.__len__() + 1, 1) + self.file_block.extend(filename.encode('utf-8')) + self.put_int(0, 1) # null byte ending + self.put_int(file['size'], 2) + self.file_block.extend(file['contents']) + + os.chdir(start_path) + + identifier = all_files[0]['filename'] + + full_file = bytearray() + full_file.extend([0xFE, 0x15]) # file handle + full_file.extend([0x03, 0x00]) # file version + full_file.extend(int(0).to_bytes(4, 'little')) # file offset + full_file.extend(self.file_block.__len__().to_bytes(4, 'little')) # file size + full_file.extend(self.file_block) + full_file.extend(crc32c.crc32c(self.file_block).to_bytes(4, 'little')) + + if output_file_path is None: + output_file_path = identifier + + with open(output_file_path, 'wb') as output_file: + output_file.write(full_file) + + + +def main(): + packer = Packer() + input_dir_path = None + output_file_path = None + args, remainder = getopt.getopt(sys.argv[1:], 'i:o:', ['input=', 'output=']) + for key, value in args: + if key in ['-i', '--input']: + input_dir_path = value + elif key in ['-o', '--output']: + output_file_path = value + packer.pack(input_dir_path, output_file_path) + + +if __name__ == '__main__': + main() diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2..943f0cbfa 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a2e01c0df..070cb702f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c78733..65dcd68d6 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93..6689b85be 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/main/fastlane/metadata/android/en-US/changelogs/227.txt b/src/main/fastlane/metadata/android/en-US/changelogs/227.txt new file mode 100644 index 000000000..9c20dcb28 --- /dev/null +++ b/src/main/fastlane/metadata/android/en-US/changelogs/227.txt @@ -0,0 +1,78 @@ +* Initial support for Amazfit Balance +* Initial support for Amazfit Active +* Initial support for ColaCao 2021 +* Initial support for ColaCao 2023 +* Initial support for Femometer Vinca II +* Initial support for Mijia LYWSD02MMC variant +* Initial support for Sony Wena 3 +* Experimental support for Divoom Pixoo +* Experimental support for Sony WF-1000XM5 +* Experimental support for Amazfit Active Edge +* Experimental support for Mi Band 7 Pro (Xiaomi Smart Band 7 Pro) +* Experimental support for Mi Band 8 (Xiaomi Smart Band 8) +* Experimental support for Mi Watch Lite +* Experimental support for Mi Watch Color Sport +* Experimental support for Redmi Smart Band 2 +* Experimental support for Redmi Watch 3 Active +* Experimental support for Xiaomi Watch S1 Active +* Amazfit Band 7: Add alexa menu entries +* Amazfit GTR 3 Pro: Fix firmware and watchface upload +* Amazfit T-Rex: Fix activity summary parsing +* Amazfit T-Rex Pro: Add activate display on lift sensitivity +* AsteroidOS: Add more supported watch models +* AsteroidOS: Fix media info +* AsteroidOS: Fix notification dismissal +* Bangle.js: Add loyalty cards integration with Catima +* Bangle.js: Ensure SMS messages have src field set to "SMS Message" +* Bangle.js: Fix GPS speed +* Bangle.js: Improve handling of chinese characters +* Bangle.js: Lower threshold for low battery warning +* Bangle.js: Recover from device initialization failure +* Casio GBX100/GBD-200: Fix first connect +* Casio GB5600/6900/STB-1000: Fix pairing +* Casio GDB-200: Fix notification timestamp +* Casio GDB-200: Fixed notification categories and default category +* Casio GDB-200: Allow preview of notification message alongside title +* Casio GDB-200: Fixed find my phone feature +* Intent API: Add debug action for test new function +* Fossil/Skagen Hybrids: Add new navigation app +* Fossil/Skagen Hybrids: Allow configuring call rejection method +* Fossil/Skagen Hybrids: Fix some preference crashes on the nightly +* Fossil/Skagen Hybrids: Reduce toasts on release builds +* Fossil/Skagen Hybrids: Show device specific settings in more logical order +* Huami: Toggle phone silent mode from band +* Message privacy: Add mode Hide only body +* Mijia LYWSD02: Add battery +* Mijia LYWSD02: Add low battery notification +* Mijia LYWSD02: Set temperature unit +* Mijia LYWSD02: Fix battery drain while connected +* PineTime: Display app name for VoIP app calls +* PineTime: Honor Sync time setting on connect +* PineTime: Improve notification handling +* PineTime: Reduce weather memory usage +* Withings Steel HR: Fix crash when calibrating hands on the nightly +* Zepp OS: Add blood oxygen graph +* Zepp OS: Add workout codes for hiking and outdoor swimming +* Zepp OS: Allow disabling app notifications per device +* Zepp OS: Attempt to fix activity fetch operation getting stuck +* Zepp OS: Display swimming activity data +* Zepp OS: Fix health settings on older Zepp OS versions +* Zepp OS: Fix setting of unknown button press apps +* Zepp OS: Fix sunrise and moon dates being off by local time + UTC offset +* Zepp OS: Map hiking, outdoor swimming, climbing and table tennis activity types +* Zepp OS: Toggle phone silent mode from band +* Add transliteration for Latvian, Hungarian, Common Symbols +* Allow multiple device actions to be triggered for the same event +* Allow toggling DND through device actions +* Autodetect OsmAnd package name and make it configurable +* Improve ASCII transliterator +* Make GMaps navigation handler follow the "navigation forwarding" setting +* Support selecting enabled navigation apps +* Allow ignore notifications from work profile apps +* Display alias in low battery notification +* Fix crash when pairing current device as companion +* Fix emoji when a transliterator is enabled +* Fix UV Index and rain probability for some weather apps +* Improve device discovery stability and fix freezes +* Improve Telegram and COL Reminder notifications +* Replace old-style preference switch with Material 3 switch diff --git a/src/main/fastlane/metadata/android/en-US/changelogs/228.txt b/src/main/fastlane/metadata/android/en-US/changelogs/228.txt new file mode 100644 index 000000000..d818f9535 --- /dev/null +++ b/src/main/fastlane/metadata/android/en-US/changelogs/228.txt @@ -0,0 +1,28 @@ +* Initial support for Honor Band 3,4,5,6 +* Initial support for Huawei Band 4, 4 Pro, 6, 7, 3e, 4e +* Initial support for Huawei Talk Band B6 +* Initial support for Huawei Watch GT, GT 2 +* Initial support for Mijia LYWSD03MMC +* Initial support for Nothing Ear (2) +* Initial support for Nothing Ear (Stick) +* Experimental support for Honor Band 7 +* Experimental support for Redmi Watch 2 Lite +* Experimental support for Redmi Smart Band Pro +* Casio GBX100: Add support for snooze alarm +* Fossil/Skagen Hybrids: Update navigationApp to 1.1 +* Huami: Fetch SpO2 on devices that support it +* Pebble: Attempt to fix app configuration webview +* PineTime: Add support for InfiniTime's new simple weather +* PineTime: Fix freeze and reboot when upgrading firmware +* Pixoo: Enable sending images (non-persistent) +* Pixoo: Get and send alarms +* Pixoo: Set custom device name +* Pixoo: support "clap hands to turn off screen" and "sleep after silence" settings +* Xiaomi: Improve activity and workout parsing +* Xiaomi: Improve stability and fix some crashes +* Xiaomi: Improve weather +* Xiaomi: Parse sleep stages +* Add a notifications channel for connection status notifications +* Improve automatic connection to all or previous devices +* Fix devices sometimes staying stuck in a "Connecting" state +* Map some missing Google Maps navigation actions diff --git a/src/main/fastlane/metadata/android/en-US/changelogs/229.txt b/src/main/fastlane/metadata/android/en-US/changelogs/229.txt new file mode 100644 index 000000000..fb398599b --- /dev/null +++ b/src/main/fastlane/metadata/android/en-US/changelogs/229.txt @@ -0,0 +1,23 @@ +* Initial support for Honor Magic Watch 2 +* Initial support for Mijia MHO-C303 +* Initial support for Nothing CMF Watch Pro +* Initial support for Sony WI-SP600N +* Experimental support for Redmi Watch 2 +* Experimental support for Xiaomi Smart Band 8 Pro +* Experimental support for Xiaomi Watch S1 Pro +* Experimental support for Xiaomi Watch S1 +* Experimental support for Xiaomi Watch S3 +* Galaxy Buds2 Pro: Fix recognition of some versions +* Huawei Watch GT 2: Fix pairing +* Redmi Smart Band Pro: Fix password digits +* Pebble: Fix app configuration page +* Pebble 2: Fix pairing issue +* PineTime: Fix weather forecast on InfiniTime's new simple weather +* Xiaomi: Fix sleep sometimes extending past the wakeup time +* Xiaomi: Request battery level and charging state periodically +* Xiaomi: Fix sleep stage parsing for some devices +* Zepp OS: Improve device discovery +* Zepp OS: Fix weather not working on some devices +* Zepp OS: Prevent crash when installing large firmware updates +* Fix sport activity summary group order +* Fix reconnection to devices failing occasionally