├── testCommon ├── .gitignore ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── version.properties ├── images ├── welcome.png ├── settings.png ├── notification_list.png ├── pairing_success.png ├── scan_pairing_code.png └── offer_taken_details.png ├── app ├── src │ ├── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── bisq_mark.png │ │ │ │ ├── checkmark.png │ │ │ │ ├── playstore_icon.png │ │ │ │ ├── ic_delete_white_24.xml │ │ │ │ ├── ic_debug_24.xml │ │ │ │ ├── ic_theme_white_24.xml │ │ │ │ ├── ic_restart_white_24.xml │ │ │ │ ├── ic_link_white_24.xml │ │ │ │ ├── ic_info_white_24.xml │ │ │ │ ├── ic_stat_notifications.xml │ │ │ │ ├── ic_qr_code_scanner_white_24.xml │ │ │ │ ├── circular_progressbar.xml │ │ │ │ └── ic_bisq_mark.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── theme_res.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ └── ic_launcher.xml │ │ │ ├── menu │ │ │ │ └── menu.xml │ │ │ ├── values-night │ │ │ │ └── colors.xml │ │ │ ├── xml │ │ │ │ └── settings.xml │ │ │ └── layout │ │ │ │ ├── activity_settings.xml │ │ │ │ ├── activity_notification_table.xml │ │ │ │ ├── activity_pairing_send.xml │ │ │ │ ├── notification_cell.xml │ │ │ │ └── activity_pairing_success.xml │ │ ├── assets │ │ │ └── Font Awesome 5 Free-Solid-900.otf │ │ ├── java │ │ │ └── bisq │ │ │ │ └── android │ │ │ │ ├── database │ │ │ │ ├── DebugLogLevel.kt │ │ │ │ ├── DebugLog.kt │ │ │ │ ├── DebugLogDao.kt │ │ │ │ ├── DebugLogRepository.kt │ │ │ │ ├── DebugLogDatabase.kt │ │ │ │ ├── NotificationDatabase.kt │ │ │ │ ├── BisqNotificationDao.kt │ │ │ │ ├── BisqNotification.kt │ │ │ │ └── NotificationRepository.kt │ │ │ │ ├── Constants.kt │ │ │ │ ├── model │ │ │ │ ├── DeviceStatus.kt │ │ │ │ ├── NotificationType.kt │ │ │ │ └── NotificationMessage.kt │ │ │ │ ├── ui │ │ │ │ ├── settings │ │ │ │ │ └── SettingsActivity.kt │ │ │ │ ├── UnpairedBaseActivity.kt │ │ │ │ ├── debug │ │ │ │ │ ├── DebugViewModel.kt │ │ │ │ │ └── DebugActivity.kt │ │ │ │ ├── UiUtil.kt │ │ │ │ ├── PairedBaseActivity.kt │ │ │ │ ├── notification │ │ │ │ │ ├── NotificationViewModel.kt │ │ │ │ │ └── NotificationDetailActivity.kt │ │ │ │ ├── pairing │ │ │ │ │ ├── PairingSendActivity.kt │ │ │ │ │ ├── PairingScanActivity.kt │ │ │ │ │ ├── RequestNotificationPermissionActivity.kt │ │ │ │ │ └── PairingSuccessActivity.kt │ │ │ │ ├── ThemeProvider.kt │ │ │ │ └── DialogBuilder.kt │ │ │ │ ├── util │ │ │ │ ├── DateUtil.kt │ │ │ │ ├── MaskingUtil.kt │ │ │ │ └── QrUtil.kt │ │ │ │ ├── ext │ │ │ │ ├── BroadcastReceiverExt.kt │ │ │ │ └── StringExt.kt │ │ │ │ ├── services │ │ │ │ ├── NotificationReceiver.kt │ │ │ │ ├── IntentReceiver.kt │ │ │ │ └── NotificationHandler.kt │ │ │ │ ├── Logging.kt │ │ │ │ └── Application.kt │ │ └── AndroidManifest.xml │ ├── debug │ │ └── AndroidManifest.xml │ ├── androidTest │ │ └── java │ │ │ └── bisq │ │ │ └── android │ │ │ ├── screens │ │ │ ├── elements │ │ │ │ ├── ClickableElement.kt │ │ │ │ ├── ElementById.kt │ │ │ │ ├── ElementByText.kt │ │ │ │ ├── ButtonElement.kt │ │ │ │ ├── SelectionElement.kt │ │ │ │ ├── PreferenceElement.kt │ │ │ │ ├── MenuItemElement.kt │ │ │ │ ├── Element.kt │ │ │ │ └── TextElement.kt │ │ │ ├── PairingScanScreen.kt │ │ │ ├── Screen.kt │ │ │ ├── dialogs │ │ │ │ ├── PromptDialog.kt │ │ │ │ ├── ChoicePromptDialog.kt │ │ │ │ ├── ThemePromptDialog.kt │ │ │ │ ├── Dialog.kt │ │ │ │ └── PermissionPrompt.kt │ │ │ ├── PairingSendScreen.kt │ │ │ ├── PairingSuccessScreen.kt │ │ │ ├── RequestNotificationPermissionScreen.kt │ │ │ ├── NotificationDetailScreen.kt │ │ │ ├── WelcomeScreen.kt │ │ │ ├── NotificationTableScreen.kt │ │ │ └── SettingsScreen.kt │ │ │ ├── tests │ │ │ ├── PairingScanTest.kt │ │ │ ├── PairingSendTest.kt │ │ │ ├── NotificationDetailTest.kt │ │ │ └── RequestNotificationPermissionTest.kt │ │ │ └── rules │ │ │ ├── FirebasePushNotificationTestRule.kt │ │ │ ├── ScreenshotRule.kt │ │ │ └── LazyActivityScenarioRule.kt │ └── test │ │ └── java │ │ ├── android │ │ ├── os │ │ │ └── Build.java │ │ ├── text │ │ │ └── TextUtils.java │ │ └── util │ │ │ ├── Log.java │ │ │ └── Base64.java │ │ └── bisq │ │ └── android │ │ └── tests │ │ ├── ext │ │ └── StringExtTest.kt │ │ └── util │ │ ├── DateUtilTest.kt │ │ ├── QrUtilTest.kt │ │ └── MaskingUtilTest.kt └── proguard-rules.pro ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── .snyk ├── docs └── tests │ ├── README.md │ ├── privacy.feature │ ├── accessibility.feature │ ├── pairing.feature │ └── notifications.feature ├── .github ├── dependabot.yml ├── actions │ └── get-avd-info │ │ └── action.yml └── workflows │ └── release.yaml ├── .gitattributes ├── .editorconfig ├── gradle.properties ├── README.md └── gradlew.bat /testCommon/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':testCommon' 3 | -------------------------------------------------------------------------------- /version.properties: -------------------------------------------------------------------------------- 1 | majorVersion=1 2 | minorVersion=3 3 | patchVersion=7 4 | buildNumber= 5 | -------------------------------------------------------------------------------- /images/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/images/welcome.png -------------------------------------------------------------------------------- /images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/images/settings.png -------------------------------------------------------------------------------- /images/notification_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/images/notification_list.png -------------------------------------------------------------------------------- /images/pairing_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/images/pairing_success.png -------------------------------------------------------------------------------- /images/scan_pairing_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/images/scan_pairing_code.png -------------------------------------------------------------------------------- /images/offer_taken_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/images/offer_taken_details.png -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/bisq_mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/drawable/bisq_mark.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/drawable/checkmark.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | *.iml 4 | app/build/ 5 | app/release/ 6 | app/google-services.json 7 | build/ 8 | local.properties 9 | snyk 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/playstore_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/drawable/playstore_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/assets/Font Awesome 5 Free-Solid-900.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/assets/Font Awesome 5 Free-Solid-900.otf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bisq-network/bisqremote_Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | ignore: 2 | SNYK-JAVA-COMGOOGLECODEGSON-1730327: 3 | - androidx.room:room-compiler@2.4.2 > androidx.room:room-migration@2.4.2 > com.google.code.gson:gson@2.8.0: 4 | reason: 'Currently no direct upgrade or patch' 5 | expires: '2022-10-01T00:00:00.000Z' 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Mar 28 15:32:07 PDT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /docs/tests/README.md: -------------------------------------------------------------------------------- 1 | # Manual Tests 2 | 3 | The test scenarios detailed in these feature files are either not possible, not easy, or not worth 4 | the effort to automate so must be executed manually. If possible, they should be performed using a 5 | real Android device as opposed to an emulator. 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | - package-ecosystem: "gradle" 8 | directory: "/" 9 | schedule: 10 | interval: "monthly" 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and normalize line endings to LF 2 | # This will handle all files NOT found below 3 | * text=auto 4 | 5 | # These binary files should be left untouched 6 | # (binary is a macro for -text -diff) 7 | *.bmp binary 8 | *.gif binary 9 | *.ico binary 10 | *.jar binary 11 | *.jpg binary 12 | *.jpeg binary 13 | *.png binary 14 | -------------------------------------------------------------------------------- /docs/tests/privacy.feature: -------------------------------------------------------------------------------- 1 | Feature: Privacy 2 | Privacy is of utmost importance. It is vital that no unnecessary data is retained. 3 | 4 | Scenario: App data is erased when deleting the app 5 | Given the app has been paired 6 | And the app has received notifications 7 | When deleting the app 8 | And reinstalling the app 9 | Then the app will load the Welcome view 10 | And the previous pairing and notifications will have been deleted 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_white_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_debug_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/theme_res.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @string/dark_theme 5 | @string/light_theme 6 | @string/system_theme 7 | 8 | 9 | @string/dark_theme_preference_value 10 | @string/light_theme_preference_value 11 | @string/system_theme_preference_value 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_theme_white_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_restart_white_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_link_white_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info_white_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [.idea/codeStyles/*.xml] 12 | indent_size = 2 13 | insert_final_newline = false 14 | 15 | [*.{kt,kts}] 16 | max_line_length = 120 17 | ktlint_code_style = android_studio 18 | ktlint_standard_import-ordering = disabled 19 | ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset 20 | ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset 21 | ktlint_standard_trailing-comma-on-call-site = disabled 22 | ktlint_standard_trailing-comma-on-declaration-site = disabled 23 | ktlint_standard_function-signature = disabled 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stat_notifications.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /testCommon/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_qr_code_scanner_white_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | android.nonFinalResIds=false 10 | android.nonTransitiveRClass=false 11 | android.useAndroidX=true 12 | org.gradle.jvmargs=-Xmx1536m 13 | # When configured, Gradle will run in incubating parallel mode. 14 | # This option should only be used with decoupled projects. More details, visit 15 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 16 | # org.gradle.parallel=true 17 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/ClickableElement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | interface ClickableElement { 21 | fun click() 22 | } 23 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -dontwarn com.google.firebase.messaging.TopicOperation$TopicOperations 24 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/DebugLogLevel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | enum class DebugLogLevel { 21 | DEBUG, 22 | INFO, 23 | WARN, 24 | ERROR 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/Constants.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android 19 | 20 | const val BISQ_NETWORK_URL = "https://bisq.network" 21 | const val BISQ_MOBILE_URL = "https://docs.bisq.network/bisq-mobile" 22 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/model/DeviceStatus.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.model 19 | 20 | enum class DeviceStatus { 21 | UNPAIRED, 22 | PAIRED, 23 | ERASED, 24 | REMOTE_ERASED, 25 | NEEDS_REPAIR 26 | } 27 | -------------------------------------------------------------------------------- /app/src/test/java/android/os/Build.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package android.os; 19 | 20 | public class Build { 21 | public static final String MANUFACTURER = "My Manufacturer"; 22 | public static final String MODEL = "My Model"; 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 14 | 17 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/test/java/android/text/TextUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package android.text; 19 | 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | public class TextUtils { 23 | public static boolean isEmpty(@Nullable CharSequence str) { 24 | return str == null || str.length() == 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/model/NotificationType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.model 19 | 20 | enum class NotificationType { 21 | // setup 22 | SETUP_CONFIRMATION, 23 | ERASE, 24 | 25 | // from Bisq 26 | TRADE, 27 | DISPUTE, 28 | OFFER, 29 | PRICE, 30 | MARKET 31 | } 32 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/PairingScanScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.R 21 | import bisq.android.screens.elements.ButtonElement 22 | 23 | class PairingScanScreen : Screen() { 24 | val noWebcamButton = ButtonElement(R.id.pairing_scan_no_webcam_button) 25 | } 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/Screen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import android.content.Context 21 | import androidx.test.core.app.ApplicationProvider 22 | 23 | abstract class Screen { 24 | protected val applicationContext: Context = ApplicationProvider.getApplicationContext() 25 | } 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/dialogs/PromptDialog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.dialogs 19 | 20 | import android.R 21 | import bisq.android.screens.elements.ButtonElement 22 | 23 | open class PromptDialog(message: String) : Dialog(message) { 24 | val dismissButton = ButtonElement(R.id.button1) 25 | } 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/PairingSendScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.R 21 | import bisq.android.screens.elements.ButtonElement 22 | 23 | class PairingSendScreen : Screen() { 24 | val sendPairingTokenButton = ButtonElement(R.id.pairing_send_pairing_token_button) 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/model/NotificationMessage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.model 19 | 20 | data class NotificationMessage(val magicValue: String, val initializationVector: String, val encryptedPayload: String) { 21 | companion object { 22 | const val BISQ_MESSAGE_ANDROID_MAGIC = "BisqMessageAndroid" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/dialogs/ChoicePromptDialog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.dialogs 19 | 20 | import android.R 21 | import bisq.android.screens.elements.ButtonElement 22 | 23 | class ChoicePromptDialog(message: String) : Dialog(message) { 24 | val positiveButton = ButtonElement(R.id.button1) 25 | val negativeButton = ButtonElement(R.id.button2) 26 | } 27 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/ElementById.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import android.view.View 21 | import androidx.test.espresso.matcher.ViewMatchers 22 | import org.hamcrest.Matcher 23 | 24 | abstract class ElementById(private val id: Int) : Element() { 25 | override fun getViewMatcher(): Matcher? = ViewMatchers.withId(id) 26 | } 27 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/ElementByText.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import android.view.View 21 | import androidx.test.espresso.matcher.ViewMatchers 22 | import org.hamcrest.Matcher 23 | 24 | abstract class ElementByText(private val text: String) : Element() { 25 | override fun getViewMatcher(): Matcher? = ViewMatchers.withText(text) 26 | } 27 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/PairingSuccessScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.R 21 | import bisq.android.screens.dialogs.PermissionPrompt 22 | import bisq.android.screens.elements.ButtonElement 23 | 24 | class PairingSuccessScreen : Screen() { 25 | val pairingCompleteButton = ButtonElement(R.id.pairing_scan_pairing_complete_button) 26 | val permissionPrompt = PermissionPrompt() 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/settings/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui.settings 19 | 20 | import bisq.android.R 21 | import bisq.android.ui.PairedBaseActivity 22 | 23 | class SettingsActivity : PairedBaseActivity() { 24 | 25 | override fun getRootLayoutId() = R.id.settings_layout 26 | override fun getStatusBarScrimId() = R.id.settings_status_bar_background 27 | 28 | override fun initView() { 29 | setContentView(R.layout.activity_settings) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/DebugLog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import androidx.room.Entity 21 | import androidx.room.PrimaryKey 22 | import bisq.android.util.DateUtil 23 | 24 | @Entity 25 | data class DebugLog( 26 | @PrimaryKey(autoGenerate = true) 27 | var id: Int = 0, 28 | var timestamp: Long, 29 | var level: DebugLogLevel, 30 | var text: String 31 | ) { 32 | override fun toString(): String = "${DateUtil.format(timestamp)} [$level] $text" 33 | } 34 | -------------------------------------------------------------------------------- /app/src/test/java/android/util/Log.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package android.util; 19 | 20 | public final class Log { 21 | public static int i(String tag, String msg) { 22 | System.out.print(tag + msg); 23 | return 0; 24 | } 25 | 26 | public static int w(String tag, String msg) { 27 | System.out.print(tag + msg); 28 | return 0; 29 | } 30 | 31 | public static int e(String tag, String msg) { 32 | System.out.print(tag + msg); 33 | return 0; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/DebugLogDao.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import androidx.lifecycle.LiveData 21 | import androidx.room.Dao 22 | import androidx.room.Insert 23 | import androidx.room.Query 24 | 25 | @Dao 26 | interface DebugLogDao { 27 | @get:Query("SELECT * FROM DebugLog ORDER BY timestamp DESC") 28 | val all: LiveData> 29 | 30 | @Insert 31 | suspend fun insert(debugLog: DebugLog): Long 32 | 33 | @Query("DELETE FROM DebugLog") 34 | suspend fun deleteAll() 35 | } 36 | -------------------------------------------------------------------------------- /app/src/test/java/android/util/Base64.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package android.util; 19 | 20 | public class Base64 { 21 | 22 | public static String encodeToString(byte[] input, int flags) { 23 | return java.util.Base64.getEncoder().encodeToString(input); 24 | } 25 | 26 | public static byte[] encode(byte[] input, int flags) { 27 | return java.util.Base64.getEncoder().encode(input); 28 | } 29 | 30 | public static byte[] decode(String str, int flags) { 31 | return java.util.Base64.getDecoder().decode(str); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /testCommon/build.gradle: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile 3 | 4 | apply plugin: 'com.android.library' 5 | apply plugin: 'kotlin-android' 6 | apply plugin: 'org.jlleitschuh.gradle.ktlint' 7 | apply plugin: 'io.gitlab.arturbosch.detekt' 8 | 9 | repositories { 10 | google() 11 | mavenCentral() 12 | } 13 | 14 | android { 15 | namespace 'bisq.android.testCommon' 16 | compileSdk 36 17 | 18 | defaultConfig { 19 | minSdk 26 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_17 24 | targetCompatibility JavaVersion.VERSION_17 25 | } 26 | 27 | packagingOptions { 28 | resources.excludes.add("META-INF/*") 29 | } 30 | } 31 | 32 | tasks.withType(KotlinJvmCompile).configureEach { 33 | compilerOptions { 34 | jvmTarget.set(JvmTarget.JVM_17) 35 | } 36 | } 37 | 38 | dependencies { 39 | detektPlugins('io.gitlab.arturbosch.detekt:detekt-formatting:1.23.8') 40 | 41 | implementation platform('com.google.firebase:firebase-bom:34.1.0') 42 | implementation 'com.google.firebase:firebase-messaging:25.0.0' 43 | 44 | implementation 'io.mockk:mockk:1.14.5' 45 | implementation 'junit:junit:4.13.2' 46 | } 47 | -------------------------------------------------------------------------------- /docs/tests/accessibility.feature: -------------------------------------------------------------------------------- 1 | Feature: Accessibility 2 | Accessibility options alter the presentation of components within the UI. As a result, it is 3 | important that the app behaves appropriately when using accessibility options. 4 | 5 | Scenario: Changing system font settings is reflected within the app 6 | Given the system font settings have been changed 7 | When navigating the app 8 | Then the app uses the updated font settings and behaves appropriately 9 | 10 | Scenario: Increasing system text/zoom settings is reflected within the app 11 | Given the system text/zoom settings have been increased 12 | When navigating the app 13 | Then the app uses the updated text/zoom settings and behaves appropriately 14 | 15 | Scenario: Decreasing system text/zoom settings is reflected within the app 16 | Given the system text/zoom settings have been decreased 17 | When navigating the app 18 | Then the app uses the updated text/zoom settings and behaves appropriately 19 | 20 | Scenario: Changing system color settings is reflected within the app 21 | Given the system color settings have been changed 22 | When navigating the app 23 | Then the app uses the updated color settings and behaves appropriately 24 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/RequestNotificationPermissionScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.R 21 | import bisq.android.screens.dialogs.PermissionPrompt 22 | import bisq.android.screens.elements.ButtonElement 23 | 24 | class RequestNotificationPermissionScreen : Screen() { 25 | val enableNotificationsButton = ButtonElement(R.id.request_notification_permission_button) 26 | val skipPermissionButton = ButtonElement(R.id.skip_request_notification_permission_button) 27 | val permissionPrompt = PermissionPrompt() 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/UnpairedBaseActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui 19 | 20 | import android.content.Intent 21 | import bisq.android.ui.pairing.PairingSuccessActivity 22 | 23 | abstract class UnpairedBaseActivity : BaseActivity() { 24 | 25 | fun pairingConfirmed() { 26 | this.runOnUiThread { 27 | playTone() 28 | } 29 | val intent = Intent(this, PairingSuccessActivity::class.java) 30 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) 31 | startActivity(intent) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #25B135 4 | #FFFFFF 5 | #00530F 6 | #FFFFFF 7 | #DDDDDD 8 | #25B135 9 | #A2DA8D 10 | 11 | #53634E 12 | #FFFFFF 13 | #D6E8CE 14 | #111F0F 15 | #DDDDDD 16 | #8E8E8E 17 | 18 | #FCFDF6 19 | #1A1C19 20 | #B7B7B7 21 | 22 | #FCFDF6 23 | #1A1C19 24 | 25 | #FFB4AB 26 | #690005 27 | #93000A 28 | #FFDAD6 29 | 30 | #000000 31 | #808080 32 | 33 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/ButtonElement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import androidx.test.espresso.Espresso 21 | import androidx.test.espresso.action.ViewActions 22 | import androidx.test.espresso.assertion.ViewAssertions 23 | import androidx.test.espresso.matcher.ViewMatchers 24 | 25 | class ButtonElement(private val id: Int) : 26 | ElementById(id), 27 | ClickableElement { 28 | 29 | override fun click() { 30 | Espresso.onView(ViewMatchers.withId(id)) 31 | .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 32 | .perform(ViewActions.click()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #25B135 4 | #FFFFFF 5 | #00530F 6 | #FFFFFF 7 | #DDDDDD 8 | #25B135 9 | #A2DA8D 10 | 11 | #BACCB3 12 | #253423 13 | #2B2B2B 14 | #CBCBCB 15 | #DDDDDD 16 | #8E8E8E 17 | 18 | #262626 19 | #F2F2F2 20 | #585858 21 | 22 | #262626 23 | #F2F2F2 24 | 25 | #FFB4AB 26 | #690005 27 | #93000A 28 | #FFDAD6 29 | 30 | #FFFFFF 31 | #808080 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/util/DateUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.util 19 | 20 | import java.text.SimpleDateFormat 21 | import java.util.Locale 22 | import java.util.TimeZone 23 | 24 | object DateUtil { 25 | private val LOCALE = Locale.US 26 | private const val PATTERN = "yyyy-MM-dd HH:mm:ss" 27 | 28 | fun format( 29 | date: Long, 30 | locale: Locale = LOCALE, 31 | pattern: String = PATTERN, 32 | timezone: TimeZone = TimeZone.getDefault() 33 | ): String? { 34 | val formatter = SimpleDateFormat(pattern, locale) 35 | formatter.timeZone = timezone 36 | return formatter.format(date) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/SelectionElement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import androidx.test.espresso.Espresso 21 | import androidx.test.espresso.action.ViewActions 22 | import androidx.test.espresso.assertion.ViewAssertions 23 | import androidx.test.espresso.matcher.ViewMatchers 24 | 25 | class SelectionElement(private val text: String) : 26 | ElementByText(text), 27 | ClickableElement { 28 | 29 | override fun click() { 30 | Espresso.onView(ViewMatchers.withText(text)) 31 | .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 32 | .perform(ViewActions.click()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/PreferenceElement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import androidx.test.espresso.Espresso 21 | import androidx.test.espresso.action.ViewActions 22 | import androidx.test.espresso.assertion.ViewAssertions 23 | import androidx.test.espresso.matcher.ViewMatchers 24 | 25 | class PreferenceElement(private val text: String) : 26 | ElementByText(text), 27 | ClickableElement { 28 | 29 | override fun click() { 30 | Espresso.onView(ViewMatchers.withText(text)) 31 | .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 32 | .perform(ViewActions.click()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/NotificationDetailScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.R 21 | import bisq.android.screens.elements.ButtonElement 22 | import bisq.android.screens.elements.TextElement 23 | 24 | class NotificationDetailScreen : Screen() { 25 | val title = TextElement(R.id.notification_detail_title) 26 | val message = TextElement(R.id.notification_detail_message) 27 | val action = TextElement(R.id.notification_detail_action) 28 | val eventTime = TextElement(R.id.notification_detail_event_time) 29 | val receivedTime = TextElement(R.id.notification_detail_received_time) 30 | val deleteButton = ButtonElement(R.id.notification_delete_button) 31 | } 32 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/tests/PairingScanTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.tests 19 | 20 | import androidx.test.espresso.intent.Intents.intended 21 | import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent 22 | import androidx.test.ext.junit.runners.AndroidJUnit4 23 | import bisq.android.ui.pairing.PairingSendActivity 24 | import org.junit.Test 25 | import org.junit.runner.RunWith 26 | 27 | @RunWith(AndroidJUnit4::class) 28 | class PairingScanTest : BaseTest() { 29 | @Test 30 | fun clickNoWebcamButtonLoadsPairingSendActivity() { 31 | pairingScanActivityRule.launch() 32 | 33 | pairingScanScreen.noWebcamButton.click() 34 | intended(hasComponent(PairingSendActivity::class.java.name)) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bisq Notification Android App 2 | 3 | Since Bisq is a desktop-based application, this Android app enables you to pair it with your desktop 4 | application and receive important notifications such as trade updates and offer alerts when you are 5 | not near your computer. 6 | 7 | ## Prerequisites 8 | 9 | In order to pair the app and receive notifications, you will need to obtain an appropriate 10 | `google-services.json` file and place it under the app/ directory. Refer to 11 | [firebase documentation](https://firebase.google.com/docs/android/setup#add-config-file) 12 | for more information. 13 | 14 | > Note, the `google-services.json` file needs to correspond to the `fcmServiceAccountKey.json` 15 | > used by the [bisq-relay](https://github.com/bisq-network/bisq-relay) service. 16 | 17 | ## Architectural Design 18 | 19 | For information on the architectural design, refer to the 20 | [Bisq Remote Specification](https://github.com/bisq-network/bisqremote/wiki/Specification). 21 | 22 | ## Screenshots 23 | 24 | Welcome 25 | Scan Pairing Code 26 | Pairing Success 27 | Notification List 28 | Offer Taken Details 29 | Settings 30 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ext/BroadcastReceiverExt.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ext 19 | 20 | import android.content.BroadcastReceiver 21 | import kotlinx.coroutines.CoroutineScope 22 | import kotlinx.coroutines.DelicateCoroutinesApi 23 | import kotlinx.coroutines.GlobalScope 24 | import kotlinx.coroutines.launch 25 | 26 | @OptIn(DelicateCoroutinesApi::class) 27 | fun BroadcastReceiver.goAsync( 28 | coroutineScope: CoroutineScope = GlobalScope, 29 | block: suspend () -> Unit 30 | ) { 31 | val result = goAsync() 32 | coroutineScope.launch { 33 | try { 34 | block() 35 | } finally { 36 | // Always call finish(), even if the coroutineScope was cancelled 37 | result?.finish() 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/util/MaskingUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.util 19 | 20 | object MaskingUtil { 21 | @Suppress("ReturnCount") 22 | fun maskSensitive(value: String?, visibleChars: Int = 5, maskChar: Char = '*'): String { 23 | if (value.isNullOrEmpty()) return value ?: "" 24 | 25 | // If the value is too short to be masked, mask the whole thing 26 | val minVisible = visibleChars * 2 27 | if (value.length <= minVisible) { 28 | return maskChar.toString().repeat(value.length) 29 | } 30 | 31 | val firstPart = value.take(visibleChars) 32 | val lastPart = value.takeLast(visibleChars) 33 | val maskedMiddle = maskChar.toString().repeat(value.length - (visibleChars * 2)) 34 | 35 | return "$firstPart$maskedMiddle$lastPart" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/circular_progressbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 16 | 22 | 23 | 24 | 25 | 26 | 31 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/DebugLogRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import android.content.Context 21 | import androidx.lifecycle.LiveData 22 | import kotlinx.coroutines.coroutineScope 23 | import kotlinx.coroutines.launch 24 | 25 | class DebugLogRepository(context: Context) { 26 | 27 | private val debugLogDao: DebugLogDao 28 | 29 | val allLogs: LiveData> 30 | 31 | init { 32 | val db = DebugLogDatabase.getDatabase(context) 33 | debugLogDao = db.debugLogDao() 34 | allLogs = debugLogDao.all 35 | } 36 | 37 | suspend fun insert(debugLog: DebugLog) = coroutineScope { 38 | launch { 39 | debugLogDao.insert(debugLog) 40 | } 41 | } 42 | 43 | suspend fun deleteAll() = coroutineScope { 44 | launch { debugLogDao.deleteAll() } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/debug/DebugViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui.debug 19 | 20 | import android.app.Application 21 | import androidx.lifecycle.AndroidViewModel 22 | import androidx.lifecycle.LiveData 23 | import androidx.lifecycle.viewModelScope 24 | import bisq.android.database.DebugLog 25 | import bisq.android.database.DebugLogRepository 26 | import kotlinx.coroutines.launch 27 | 28 | class DebugViewModel(application: Application) : AndroidViewModel(application) { 29 | 30 | private val repository: DebugLogRepository = DebugLogRepository(application) 31 | 32 | var allLogs: LiveData> = repository.allLogs 33 | 34 | fun insert(debugLog: DebugLog) = viewModelScope.launch { 35 | repository.insert(debugLog) 36 | } 37 | 38 | fun nukeTable() = viewModelScope.launch { 39 | repository.deleteAll() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ext/StringExt.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ext 19 | 20 | import android.text.TextUtils 21 | 22 | @Suppress("MagicNumber") 23 | fun String.hexStringToByteArray() = ByteArray(this.length / 2) { 24 | this.substring(it * 2, it * 2 + 2).toInt(16).toByte() 25 | } 26 | 27 | fun String.capitalizeEachWord(): String { 28 | if (TextUtils.isEmpty(this)) { 29 | return this 30 | } 31 | val arr = this.toCharArray() 32 | var capitalizeNext = true 33 | 34 | val phrase = StringBuilder() 35 | for (c in arr) { 36 | if (capitalizeNext && Character.isLetter(c)) { 37 | phrase.append(Character.toUpperCase(c)) 38 | capitalizeNext = false 39 | continue 40 | } else if (Character.isWhitespace(c)) { 41 | capitalizeNext = true 42 | } 43 | phrase.append(c) 44 | } 45 | 46 | return phrase.toString() 47 | } 48 | -------------------------------------------------------------------------------- /.github/actions/get-avd-info/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Get AVD Info' 2 | description: 'Get the Android Virtual Device (AVD) info based on its API level.' 3 | inputs: 4 | api-level: 5 | description: The API level for which to retrieve AVD info 6 | required: true 7 | outputs: 8 | target: 9 | description: Target of the system image 10 | value: ${{ steps.get-avd-target.outputs.target }} 11 | arch: 12 | description: CPU architecture of the system image 13 | value: ${{ steps.get-avd-arch.outputs.arch }} 14 | runs: 15 | using: "composite" 16 | steps: 17 | # Prefer ATD system images available in API 30+ as they are optimized for headless tests. 18 | # Google Play services is required and is available in the google_atd and google_apis images. 19 | # Note: 20 | # - API 27 does not provide a google_apis system image. 21 | # - Newer API's may not yet provide an ATD system image. 22 | - id: get-avd-target 23 | shell: bash 24 | run: | 25 | set -euo pipefail 26 | api="${{ inputs.api-level }}" 27 | target="google_apis" 28 | if [[ "$api" -eq 27 ]]; then 29 | target="default" 30 | elif [[ "$api" -ge 30 ]] && sdkmanager --list | grep -q "system-images;android-$api;google_atd;"; then 31 | target="google_atd" 32 | fi 33 | echo "target=$target" >> "$GITHUB_OUTPUT" 34 | # Prefer x86_64 architecture 35 | - id: get-avd-arch 36 | shell: bash 37 | run: echo "arch=x86_64" >> $GITHUB_OUTPUT 38 | -------------------------------------------------------------------------------- /app/src/main/res/xml/settings.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | 17 | 18 | 22 | 23 | 27 | 28 | 32 | 33 | 37 | 38 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/MenuItemElement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import androidx.test.espresso.Espresso 21 | import androidx.test.espresso.action.ViewActions 22 | import androidx.test.espresso.assertion.ViewAssertions 23 | import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup 24 | import androidx.test.espresso.matcher.ViewMatchers 25 | import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation 26 | 27 | class MenuItemElement(private val text: String) : 28 | ElementByText(text), 29 | ClickableElement { 30 | 31 | override fun click() { 32 | Espresso.openActionBarOverflowOrOptionsMenu(getInstrumentation().targetContext) 33 | Espresso.onView(ViewMatchers.withText(text)) 34 | .inRoot(isPlatformPopup()) 35 | .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 36 | .perform(ViewActions.click()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/dialogs/ThemePromptDialog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.dialogs 19 | 20 | import android.content.Context 21 | import androidx.test.core.app.ApplicationProvider 22 | import bisq.android.R 23 | import bisq.android.screens.elements.ButtonElement 24 | import bisq.android.screens.elements.SelectionElement 25 | 26 | class ThemePromptDialog : PromptDialog(message) { 27 | val darkThemeSelection = SelectionElement(applicationContext.getString(R.string.dark_theme)) 28 | val lightThemeSelection = SelectionElement(applicationContext.getString(R.string.light_theme)) 29 | val systemThemeSelection = SelectionElement(applicationContext.getString(R.string.system_theme)) 30 | val cancelButton = ButtonElement(android.R.id.button2) 31 | 32 | companion object { 33 | private val applicationContext: Context = ApplicationProvider.getApplicationContext() 34 | val message: String = applicationContext.getString(R.string.theme) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/WelcomeScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.BISQ_MOBILE_URL 21 | import bisq.android.R 22 | import bisq.android.screens.dialogs.ChoicePromptDialog 23 | import bisq.android.screens.dialogs.PromptDialog 24 | import bisq.android.screens.elements.ButtonElement 25 | 26 | class WelcomeScreen : Screen() { 27 | val pairButton = ButtonElement(R.id.welcome_pair_button) 28 | val learnMoreButton = ButtonElement(R.id.welcome_learn_more_button) 29 | val alertDialogGooglePlayServicesUnavailable = PromptDialog( 30 | applicationContext.resources.getString(R.string.google_play_services_unavailable) 31 | ) 32 | val alertDialogCannotRetrieveDeviceToken = ChoicePromptDialog( 33 | applicationContext.resources.getString(R.string.cannot_retrieve_fcm_token) 34 | ) 35 | val alertDialogLoadBisqMobileUrl = ChoicePromptDialog( 36 | applicationContext.resources.getString(R.string.load_web_page_confirmation, BISQ_MOBILE_URL) 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/dialogs/Dialog.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.dialogs 19 | 20 | import android.content.Context 21 | import androidx.test.core.app.ApplicationProvider 22 | import androidx.test.espresso.Espresso.onView 23 | import androidx.test.espresso.NoMatchingViewException 24 | import androidx.test.espresso.assertion.ViewAssertions.matches 25 | import androidx.test.espresso.matcher.ViewMatchers.Visibility 26 | import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility 27 | import androidx.test.espresso.matcher.ViewMatchers.withText 28 | 29 | abstract class Dialog(private val message: String) { 30 | protected val applicationContext: Context = ApplicationProvider.getApplicationContext() 31 | 32 | fun isDisplayed(): Boolean = 33 | try { 34 | onView(withText(message)).check( 35 | matches(withEffectiveVisibility(Visibility.VISIBLE)) 36 | ) 37 | true 38 | } catch (_: NoMatchingViewException) { 39 | false 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/Element.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import android.view.View 21 | import androidx.test.espresso.Espresso 22 | import androidx.test.espresso.assertion.ViewAssertions 23 | import androidx.test.espresso.matcher.ViewMatchers 24 | import org.hamcrest.Matcher 25 | 26 | abstract class Element { 27 | abstract fun getViewMatcher(): Matcher? 28 | 29 | fun isDisplayed(): Boolean { 30 | try { 31 | Espresso.onView(getViewMatcher()) 32 | .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 33 | } catch (e: AssertionError) { 34 | return false 35 | } 36 | return true 37 | } 38 | 39 | fun isEnabled(): Boolean { 40 | try { 41 | Espresso.onView(getViewMatcher()) 42 | .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) 43 | .check(ViewAssertions.matches(ViewMatchers.isEnabled())) 44 | } catch (e: AssertionError) { 45 | return false 46 | } 47 | return true 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/elements/TextElement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.elements 19 | 20 | import android.view.View 21 | import android.widget.TextView 22 | import androidx.test.espresso.Espresso 23 | import androidx.test.espresso.UiController 24 | import androidx.test.espresso.ViewAction 25 | import androidx.test.espresso.matcher.ViewMatchers 26 | import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom 27 | import org.hamcrest.Matcher 28 | 29 | class TextElement(private val id: Int) : ElementById(id) { 30 | fun getText(): String { 31 | var text = String() 32 | Espresso.onView(ViewMatchers.withId(id)).perform(object : ViewAction { 33 | override fun getConstraints(): Matcher = isAssignableFrom(TextView::class.java) 34 | 35 | override fun getDescription(): String = "Text of the view" 36 | 37 | override fun perform(uiController: UiController, view: View) { 38 | val tv = view as TextView 39 | text = tv.text.toString() 40 | } 41 | }) 42 | return text 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/DebugLogDatabase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import android.content.Context 21 | import androidx.room.Database 22 | import androidx.room.Room 23 | import androidx.room.RoomDatabase 24 | 25 | @Database(entities = [DebugLog::class], version = 1, exportSchema = false) 26 | abstract class DebugLogDatabase : RoomDatabase() { 27 | 28 | abstract fun debugLogDao(): DebugLogDao 29 | 30 | companion object { 31 | 32 | private var instance: DebugLogDatabase? = null 33 | 34 | fun getDatabase(context: Context): DebugLogDatabase { 35 | if (instance == null) { 36 | synchronized(DebugLogDatabase::class.java) { 37 | if (instance == null) { 38 | instance = Room.databaseBuilder( 39 | context.applicationContext, 40 | DebugLogDatabase::class.java, 41 | "debug.db" 42 | ).build() 43 | } 44 | } 45 | } 46 | return instance!! 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/NotificationTableScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.R 21 | import bisq.android.screens.dialogs.ChoicePromptDialog 22 | import bisq.android.screens.elements.MenuItemElement 23 | import bisq.android.screens.elements.RecyclerViewElement 24 | 25 | class NotificationTableScreen : Screen() { 26 | val addExampleNotificationsMenuItem = 27 | MenuItemElement(applicationContext.resources.getString(R.string.button_add_example_notifications)) 28 | val markAllAsReadMenuItem = 29 | MenuItemElement(applicationContext.resources.getString(R.string.button_mark_as_read)) 30 | val deleteAllMenuItem = 31 | MenuItemElement(applicationContext.resources.getString(R.string.button_delete_notifications)) 32 | val settingsMenuItem = 33 | MenuItemElement(applicationContext.resources.getString(R.string.settings)) 34 | val notificationRecylerView = RecyclerViewElement(R.id.notification_table_recycler_view) 35 | val alertDialogDeleteAll = ChoicePromptDialog( 36 | applicationContext.resources.getString(R.string.delete_all_notifications_confirmation) 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/UiUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui 19 | 20 | import android.content.ActivityNotFoundException 21 | import android.content.Context 22 | import android.content.Intent 23 | import android.net.Uri 24 | import android.widget.Toast 25 | import bisq.android.R 26 | 27 | object UiUtil { 28 | fun loadWebPage(context: Context, uri: String) { 29 | DialogBuilder.choicePrompt( 30 | context, 31 | context.getString(R.string.confirm), 32 | context.getString(R.string.load_web_page_confirmation, uri), 33 | context.getString(R.string.yes), 34 | context.getString(R.string.no), 35 | { _, _ -> 36 | try { 37 | context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(uri))) 38 | } catch (ignored: ActivityNotFoundException) { 39 | Toast.makeText( 40 | context, 41 | context.getString(R.string.cannot_launch_browser), 42 | Toast.LENGTH_LONG 43 | ).show() 44 | } 45 | } 46 | ).show() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/NotificationDatabase.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import android.content.Context 21 | import androidx.room.Database 22 | import androidx.room.Room 23 | import androidx.room.RoomDatabase 24 | 25 | @Database(entities = [BisqNotification::class], version = 1, exportSchema = false) 26 | abstract class NotificationDatabase : RoomDatabase() { 27 | 28 | abstract fun bisqNotificationDao(): BisqNotificationDao 29 | 30 | companion object { 31 | 32 | private var instance: NotificationDatabase? = null 33 | 34 | fun getDatabase(context: Context): NotificationDatabase { 35 | if (instance == null) { 36 | synchronized(NotificationDatabase::class.java) { 37 | if (instance == null) { 38 | instance = Room.databaseBuilder( 39 | context.applicationContext, 40 | NotificationDatabase::class.java, 41 | "notifications.db" 42 | ).build() 43 | } 44 | } 45 | } 46 | return instance!! 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/PairedBaseActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui 19 | 20 | import android.content.Intent 21 | import android.widget.Toast 22 | import bisq.android.model.Device 23 | import bisq.android.model.DeviceStatus 24 | import bisq.android.ui.welcome.WelcomeActivity 25 | 26 | abstract class PairedBaseActivity : BaseActivity() { 27 | 28 | override fun onStart() { 29 | super.onStart() 30 | if (Device.instance.status != DeviceStatus.PAIRED) { 31 | val intent = Intent(this, WelcomeActivity::class.java) 32 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) 33 | startActivity(intent) 34 | } 35 | } 36 | 37 | fun pairingRemoved(toastMessage: String) { 38 | this.runOnUiThread { 39 | playTone() 40 | Toast.makeText( 41 | this, 42 | toastMessage, 43 | Toast.LENGTH_LONG 44 | ).show() 45 | val intent = Intent(this, WelcomeActivity::class.java) 46 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) 47 | startActivity(intent) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | # Allows for running this workflow manually from the Actions tab 5 | workflow_dispatch: 6 | 7 | jobs: 8 | create_release: 9 | name: Create release 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 10 12 | steps: 13 | - name: Download artifacts from latest master workflow 14 | uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc 15 | with: 16 | github_token: ${{ secrets.GITHUB_TOKEN }} 17 | workflow: master.yaml 18 | workflow_conclusion: success 19 | 20 | - name: Read VERSION file 21 | id: get_version 22 | run: echo "version=$(cat VERSION/VERSION)" >> $GITHUB_OUTPUT 23 | 24 | - name: Create release 25 | id: create_release 26 | uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | with: 30 | tag_name: ${{ steps.get_version.outputs.version }} 31 | release_name: ${{ steps.get_version.outputs.version }} 32 | draft: false 33 | prerelease: false 34 | 35 | - name: Upload signed APK to release 36 | id: upload_release_asset 37 | uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | with: 41 | upload_url: ${{ steps.create_release.outputs.upload_url }} 42 | asset_path: ./bisq-notifications-release-signed.apk 43 | asset_name: bisq-notifications-release-signed.apk 44 | asset_content_type: application/zip 45 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bisq_mark.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /docs/tests/pairing.feature: -------------------------------------------------------------------------------- 1 | Feature: Pairing 2 | Pairing is the process by which you share a pairing token, generated by the app, with the Bisq 3 | client. The pairing token contains the device identifier as well as a private key to encrypt 4 | the notification messages. It is also possible to erase the pairing remotely from the Bisq 5 | client. 6 | 7 | Scenario: Erase pairing while app is running in foreground 8 | Given the app has been paired 9 | And the app is running in the foreground 10 | When clicking the erase button in either the Bisq client or bisqremote tool 11 | Then the app will load the Welcome view 12 | And show a toast message indicating the app was unpaired remotely 13 | 14 | Scenario: Erase pairing while app is running in background or is closed 15 | Given the app has been paired 16 | And the app is running in the background or is closed 17 | When clicking the erase button in either the Bisq client or bisqremote tool 18 | Then a notification will be shown in the system notification area 19 | And upon clicking the notification it will load the Welcome view 20 | And show a toast message indicating the app was unpaired remotely 21 | 22 | Scenario: Erase pairing removes existing notifications 23 | Given the app has been paired 24 | And has existing notifications 25 | When clicking the erase button in either the Bisq client or bisqremote tool 26 | And following the process to pair the app again 27 | Then all previously received notifications will not exist 28 | 29 | Scenario: Pairing after the app has been put in the background then foreground 30 | Given the app is not paired 31 | And the pairing token has been scanned or transferred 32 | And the app is placed in the background 33 | And the app is placed in the foreground 34 | When the pairing process is performed 35 | Then the app is paired successfully 36 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/tests/PairingSendTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.tests 19 | 20 | import android.content.Intent 21 | import androidx.test.espresso.intent.Intents.intended 22 | import androidx.test.espresso.intent.matcher.BundleMatchers.hasValue 23 | import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction 24 | import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtras 25 | import androidx.test.ext.junit.runners.AndroidJUnit4 26 | import bisq.android.model.Device 27 | import org.junit.Test 28 | import org.junit.runner.RunWith 29 | 30 | @RunWith(AndroidJUnit4::class) 31 | class PairingSendTest : BaseTest() { 32 | @Test 33 | fun clickSendPairingTokenButton() { 34 | Device.instance.newToken( 35 | "fnWtGaJGSByKiPwT71O3Lo:APA91bGU05lvoKxvz3Y0fnFHytSveA_juVjq2QMY3_H9URqDsEp" + 36 | "LHGbLSFBN3wY7YdHDD3w52GECwRWuKGBJm1O1f5fJhVvcr1rJxo94aDjoWwsnkVp-ecWwh5YY_MQ6LRqbWzumCeX_" 37 | ) 38 | pairingSendActivityRule.launch() 39 | 40 | pairingSendScreen.sendPairingTokenButton.click() 41 | intended(hasAction(Intent.ACTION_CHOOSER)) 42 | intended( 43 | hasExtras( 44 | hasValue( 45 | hasExtras( 46 | hasValue(Device.instance.pairingToken()) 47 | ) 48 | ) 49 | ) 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/notification/NotificationViewModel.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui.notification 19 | 20 | import android.app.Application 21 | import androidx.lifecycle.AndroidViewModel 22 | import androidx.lifecycle.LiveData 23 | import androidx.lifecycle.viewModelScope 24 | import bisq.android.database.BisqNotification 25 | import bisq.android.database.NotificationRepository 26 | import kotlinx.coroutines.launch 27 | 28 | class NotificationViewModel(application: Application) : AndroidViewModel(application) { 29 | 30 | private val repository: NotificationRepository = NotificationRepository(application) 31 | 32 | var bisqNotifications: LiveData> = repository.allBisqNotifications 33 | 34 | fun insert(bisqNotification: BisqNotification) = viewModelScope.launch { 35 | repository.insert(bisqNotification) 36 | } 37 | 38 | fun delete(bisqNotification: BisqNotification) = viewModelScope.launch { 39 | repository.delete(bisqNotification) 40 | } 41 | 42 | fun nukeTable() = viewModelScope.launch { 43 | repository.deleteAll() 44 | } 45 | 46 | fun getFromUid(uid: Int): BisqNotification? = repository.getFromUid(uid) 47 | 48 | fun markAllAsRead() = viewModelScope.launch { 49 | repository.markAllAsRead() 50 | } 51 | 52 | fun markAsRead(uid: Int) = viewModelScope.launch { 53 | repository.markAsRead(uid) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/test/java/bisq/android/tests/ext/StringExtTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.tests.ext 19 | 20 | import bisq.android.ext.capitalizeEachWord 21 | import bisq.android.ext.hexStringToByteArray 22 | import org.assertj.core.api.Assertions.assertThat 23 | import org.junit.Test 24 | 25 | class StringExtTest { 26 | @Test 27 | fun testHexStringToByteArray() { 28 | val hexString = "ace24f2c3e0848bd9e57f6b415ca08df6ec7c22692bd48a296fe4044759e5eff" 29 | val bytearray = (hexString).hexStringToByteArray() 30 | assertThat(bytearray.asList().toString()) 31 | .isEqualTo( 32 | "[-84, -30, 79, 44, 62, 8, 72, -67, -98, 87, -10, -76, 21, -54, 8, " + 33 | "-33, 110, -57, -62, 38, -110, -67, 72, -94, -106, -2, 64, 68, 117, -98, 94, -1]" 34 | ) 35 | } 36 | 37 | @Test 38 | fun testCapitalizeSingleWord() { 39 | val test = "testcapitalizestring".capitalizeEachWord() 40 | assertThat(test) 41 | .isEqualTo("Testcapitalizestring") 42 | } 43 | 44 | @Test 45 | fun testCapitalizeMultipleWords() { 46 | val test = "test capitalize string".capitalizeEachWord() 47 | assertThat(test) 48 | .isEqualTo("Test Capitalize String") 49 | } 50 | 51 | @Test 52 | fun testCapitalizeEmptyString() { 53 | val test = "".capitalizeEachWord() 54 | assertThat(test).isEmpty() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/SettingsScreen.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens 19 | 20 | import bisq.android.BISQ_MOBILE_URL 21 | import bisq.android.BISQ_NETWORK_URL 22 | import bisq.android.R 23 | import bisq.android.screens.dialogs.ChoicePromptDialog 24 | import bisq.android.screens.dialogs.ThemePromptDialog 25 | import bisq.android.screens.elements.PreferenceElement 26 | 27 | class SettingsScreen : Screen() { 28 | val themePreference = PreferenceElement(applicationContext.getString(R.string.theme)) 29 | val themePromptDialog = ThemePromptDialog() 30 | val resetPairingPreference = PreferenceElement(applicationContext.getString(R.string.reset_pairing)) 31 | val alertDialogResetPairing = ChoicePromptDialog( 32 | applicationContext.resources.getString(R.string.register_again_confirmation) 33 | ) 34 | val scanPairingTokenPreference = PreferenceElement(applicationContext.getString(R.string.scan_pairing_token)) 35 | val aboutBisqPreference = PreferenceElement(applicationContext.getString(R.string.about_bisq)) 36 | val aboutAppPreference = PreferenceElement(applicationContext.getString(R.string.about_this_app)) 37 | val alertDialogLoadBisqNetworkUrl = ChoicePromptDialog( 38 | applicationContext.resources.getString(R.string.load_web_page_confirmation, BISQ_NETWORK_URL) 39 | ) 40 | val alertDialogLoadBisqMobileUrl = ChoicePromptDialog( 41 | applicationContext.resources.getString(R.string.load_web_page_confirmation, BISQ_MOBILE_URL) 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/BisqNotificationDao.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import androidx.lifecycle.LiveData 21 | import androidx.room.Dao 22 | import androidx.room.Delete 23 | import androidx.room.Insert 24 | import androidx.room.Query 25 | 26 | @Dao 27 | interface BisqNotificationDao { 28 | @get:Query("SELECT * FROM BisqNotification ORDER BY sentDate DESC") 29 | val allData: LiveData> 30 | 31 | @Query("SELECT * FROM BisqNotification ORDER BY sentDate DESC") 32 | suspend fun getAll(): List 33 | 34 | @Query("SELECT * FROM BisqNotification WHERE uid=:uid") 35 | suspend fun getFromUid(uid: Int): BisqNotification 36 | 37 | @Insert 38 | suspend fun insert(bisqNotification: BisqNotification): Long 39 | 40 | @Query( 41 | """ 42 | DELETE FROM BisqNotification 43 | WHERE rowid NOT IN ( 44 | SELECT MIN(rowid) 45 | FROM BisqNotification 46 | GROUP BY version, type, title, message, actionRequired, txId, sentDate 47 | ) 48 | """ 49 | ) 50 | suspend fun removeDuplicates() 51 | 52 | @Delete 53 | suspend fun delete(bisqNotification: BisqNotification) 54 | 55 | @Query("DELETE FROM BisqNotification") 56 | suspend fun deleteAll() 57 | 58 | @Query("UPDATE BisqNotification SET read=1") 59 | suspend fun markAllAsRead() 60 | 61 | @Query("UPDATE BisqNotification SET read=1 WHERE uid=:uid") 62 | suspend fun markAsRead(uid: Int) 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/pairing/PairingSendActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui.pairing 19 | 20 | import android.content.Intent 21 | import android.widget.Button 22 | import android.widget.TextView 23 | import bisq.android.R 24 | import bisq.android.model.Device 25 | import bisq.android.ui.UnpairedBaseActivity 26 | 27 | class PairingSendActivity : UnpairedBaseActivity() { 28 | 29 | private lateinit var sendPairingTokenInstructions: TextView 30 | private lateinit var sendPairingTokenButton: Button 31 | 32 | override fun getRootLayoutId() = R.id.pairing_send_layout 33 | override fun getStatusBarScrimId() = R.id.pairing_send_status_bar_background 34 | 35 | override fun initView() { 36 | setContentView(R.layout.activity_pairing_send) 37 | 38 | sendPairingTokenInstructions = bind(R.id.pairing_send_pairing_token_instructions) 39 | 40 | sendPairingTokenButton = bind(R.id.pairing_send_pairing_token_button) 41 | sendPairingTokenButton.setOnClickListener { 42 | onSendPairingToken() 43 | } 44 | } 45 | 46 | private fun onSendPairingToken() { 47 | val sendIntent: Intent = Intent().apply { 48 | action = Intent.ACTION_SEND 49 | type = "text/plain" 50 | putExtra(Intent.EXTRA_SUBJECT, getString(R.string.send_pairing_subject)) 51 | putExtra(Intent.EXTRA_TEXT, Device.instance.pairingToken()) 52 | } 53 | startActivity(Intent.createChooser(sendIntent, getString(R.string.send_pairing_token))) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/screens/dialogs/PermissionPrompt.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.screens.dialogs 19 | 20 | import androidx.test.platform.app.InstrumentationRegistry 21 | import androidx.test.uiautomator.UiDevice 22 | import androidx.test.uiautomator.UiSelector 23 | 24 | class PermissionPrompt { 25 | private val device 26 | get() = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) 27 | private val textElement 28 | get() = device.findObject(UiSelector().index(1)) 29 | private val grantPermissionButton 30 | get() = device.findObject(UiSelector().textMatches("(?:Allow|ALLOW)")) 31 | private val denyPermissionButton 32 | get() = device.findObject(UiSelector().textMatches("(?:Don’t allow|DON’T ALLOW)")) 33 | 34 | fun isDisplayed(): Boolean = grantPermissionButton.exists() && denyPermissionButton.exists() 35 | 36 | fun text(): String { 37 | if (!textElement.exists()) { 38 | throw IllegalStateException("Text element does not exist") 39 | } 40 | return textElement.text 41 | } 42 | 43 | fun grantPermission() { 44 | if (!grantPermissionButton.exists()) { 45 | throw IllegalStateException("Grant permissions button does not exist") 46 | } 47 | grantPermissionButton.click() 48 | } 49 | 50 | fun denyPermission() { 51 | if (!grantPermissionButton.exists()) { 52 | throw IllegalStateException("Deny permissions button does not exist") 53 | } 54 | denyPermissionButton.click() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/BisqNotification.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import androidx.room.ColumnInfo 21 | import androidx.room.Entity 22 | import androidx.room.PrimaryKey 23 | import kotlinx.serialization.Serializable 24 | 25 | @Entity 26 | @Serializable 27 | data class BisqNotification( 28 | @PrimaryKey(autoGenerate = true) 29 | var uid: Int = 0, 30 | 31 | @ColumnInfo(name = "version") 32 | var version: Int = 0, 33 | 34 | @ColumnInfo(name = "type") 35 | var type: String? = null, 36 | 37 | @ColumnInfo(name = "title") 38 | var title: String? = null, 39 | 40 | @ColumnInfo(name = "message") 41 | var message: String? = null, 42 | 43 | @ColumnInfo(name = "actionRequired") 44 | var actionRequired: String? = null, 45 | 46 | @ColumnInfo(name = "txId") 47 | var txId: String? = null, 48 | 49 | @ColumnInfo(name = "receivedDate") 50 | var receivedDate: Long = 0, 51 | 52 | @ColumnInfo(name = "sentDate") 53 | var sentDate: Long = 0, 54 | 55 | @ColumnInfo(name = "read") 56 | var read: Boolean = false 57 | ) { 58 | override fun equals(other: Any?): Boolean { 59 | if (this === other) return true 60 | if (other !is BisqNotification) return false 61 | return version == other.version && 62 | type == other.type && 63 | title == other.title && 64 | message == other.message && 65 | actionRequired == other.actionRequired && 66 | txId == other.txId && 67 | sentDate == other.sentDate 68 | } 69 | 70 | override fun hashCode(): Int = listOf(version, type, title, message, actionRequired, txId, sentDate) 71 | .hashCode() 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/util/QrUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.util 19 | 20 | import android.graphics.Bitmap 21 | import android.graphics.Color 22 | import com.google.zxing.BarcodeFormat 23 | import com.google.zxing.common.BitMatrix 24 | import com.google.zxing.qrcode.QRCodeWriter 25 | 26 | object QrUtil { 27 | 28 | fun createQrImage(contents: String, width: Int = 1024, height: Int = 1024): Bitmap { 29 | val bitMatrix = buildBitMatrix(contents, width, height) 30 | val pixels = buildPixelArray(bitMatrix) 31 | return createBitmap(pixels, width, height) 32 | } 33 | 34 | private fun buildBitMatrix( 35 | contents: String, 36 | width: Int, 37 | height: Int 38 | ): BitMatrix { 39 | val bitMatrix: BitMatrix 40 | val writer = QRCodeWriter() 41 | bitMatrix = writer.encode(contents, BarcodeFormat.QR_CODE, width, height) 42 | return bitMatrix 43 | } 44 | 45 | private fun buildPixelArray( 46 | bitMatrix: BitMatrix 47 | ): IntArray { 48 | val pixels = IntArray(bitMatrix.width * bitMatrix.height) 49 | for (y in 0 until bitMatrix.height) { 50 | val offset = y * bitMatrix.width 51 | for (x in 0 until bitMatrix.width) { 52 | pixels[offset + x] = if (bitMatrix[x, y]) Color.BLACK else Color.WHITE 53 | } 54 | } 55 | return pixels 56 | } 57 | 58 | private fun createBitmap( 59 | pixels: IntArray, 60 | width: Int, 61 | height: Int 62 | ): Bitmap { 63 | val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565) 64 | bmp.setPixels(pixels, 0, width, 0, 0, width, height) 65 | return bmp 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/androidTest/java/bisq/android/rules/FirebasePushNotificationTestRule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.rules 19 | 20 | import android.app.Service 21 | import android.content.Context 22 | import android.content.ContextWrapper 23 | import androidx.test.platform.app.InstrumentationRegistry 24 | import com.google.firebase.messaging.FirebaseMessagingService 25 | import com.google.firebase.messaging.RemoteMessage 26 | import org.junit.rules.TestWatcher 27 | import org.junit.runner.Description 28 | 29 | /** 30 | * This rule interacts with the lifecycle of FirebaseMessagingService, 31 | * effectively simulating the receipt of push notifications. 32 | */ 33 | class FirebasePushNotificationTestRule(private val pushService: FirebaseMessagingService) : TestWatcher() { 34 | 35 | companion object { 36 | private const val FIREBASE_PUSH_TOKEN = "mocked_token_value" 37 | } 38 | 39 | override fun starting(description: Description) { 40 | super.starting(description) 41 | pushService.attachBaseContext() 42 | pushService.onCreate() 43 | pushService.onNewToken(FIREBASE_PUSH_TOKEN) 44 | } 45 | 46 | override fun finished(description: Description) { 47 | pushService.onDestroy() 48 | super.finished(description) 49 | } 50 | 51 | fun onNewToken(token: String) = pushService.onNewToken(token) 52 | 53 | fun sendPush(remoteMessage: RemoteMessage) = pushService.onMessageReceived(remoteMessage) 54 | } 55 | 56 | internal fun Service.attachBaseContext() { 57 | val context = InstrumentationRegistry.getInstrumentation().targetContext 58 | 59 | val attachBaseContextMethod = ContextWrapper::class.java.getDeclaredMethod("attachBaseContext", Context::class.java) 60 | attachBaseContextMethod.isAccessible = true 61 | 62 | attachBaseContextMethod.invoke(this, context) 63 | } 64 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/ui/ThemeProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.ui 19 | 20 | import android.app.UiModeManager 21 | import android.content.Context 22 | import androidx.appcompat.app.AppCompatDelegate 23 | import androidx.preference.PreferenceManager 24 | import bisq.android.R 25 | import java.security.InvalidParameterException 26 | 27 | class ThemeProvider(private val context: Context) { 28 | fun getThemeFromPreferences(): Int { 29 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 30 | val selectedTheme = sharedPreferences.getString( 31 | context.getString(R.string.theme_preferences_key), 32 | context.getString(R.string.system_theme_preference_value) 33 | ) 34 | 35 | return selectedTheme?.let { 36 | getTheme(it) 37 | } ?: AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM 38 | } 39 | 40 | fun getThemeDescriptionForPreference(preferenceValue: String?): String = 41 | when (preferenceValue) { 42 | context.getString(R.string.dark_theme_preference_value) -> 43 | context.getString(R.string.dark_theme_description) 44 | 45 | context.getString(R.string.light_theme_preference_value) -> 46 | context.getString(R.string.light_theme_description) 47 | 48 | else -> context.getString(R.string.system_theme_description) 49 | } 50 | 51 | fun getTheme(selectedTheme: String): Int = when (selectedTheme) { 52 | context.getString(R.string.dark_theme_preference_value) -> UiModeManager.MODE_NIGHT_YES 53 | context.getString(R.string.light_theme_preference_value) -> UiModeManager.MODE_NIGHT_NO 54 | context.getString(R.string.system_theme_preference_value) -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM 55 | else -> throw InvalidParameterException("Theme not defined for $selectedTheme") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 19 | 20 | 21 | 29 | 30 | 42 | 43 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/database/NotificationRepository.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.database 19 | 20 | import android.content.Context 21 | import androidx.lifecycle.LiveData 22 | import kotlinx.coroutines.coroutineScope 23 | import kotlinx.coroutines.launch 24 | import kotlinx.coroutines.runBlocking 25 | 26 | class NotificationRepository(context: Context) { 27 | 28 | private val bisqNotificationDao: BisqNotificationDao 29 | 30 | val allBisqNotifications: LiveData> 31 | 32 | init { 33 | val db = NotificationDatabase.getDatabase(context) 34 | bisqNotificationDao = db.bisqNotificationDao() 35 | allBisqNotifications = bisqNotificationDao.allData 36 | } 37 | 38 | suspend fun insert(bisqNotification: BisqNotification) = coroutineScope { 39 | launch { 40 | bisqNotificationDao.insert(bisqNotification) 41 | // This is a hack to prevent duplicate entries. 42 | // All other attempts at enforcing uniqueness were unsuccessful. 43 | bisqNotificationDao.removeDuplicates() 44 | } 45 | } 46 | 47 | suspend fun delete(bisqNotification: BisqNotification) = coroutineScope { 48 | launch { bisqNotificationDao.delete(bisqNotification) } 49 | } 50 | 51 | fun getFromUid(uid: Int): BisqNotification? { 52 | var x: BisqNotification? 53 | runBlocking { 54 | coroutineScope { 55 | x = bisqNotificationDao.getFromUid(uid) 56 | } 57 | } 58 | return x 59 | } 60 | 61 | suspend fun deleteAll() = coroutineScope { 62 | launch { bisqNotificationDao.deleteAll() } 63 | } 64 | 65 | suspend fun markAllAsRead() = coroutineScope { 66 | launch { bisqNotificationDao.markAllAsRead() } 67 | } 68 | 69 | suspend fun markAsRead(uid: Int) = coroutineScope { 70 | launch { bisqNotificationDao.markAsRead(uid) } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/test/java/bisq/android/tests/util/DateUtilTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.tests.util 19 | 20 | import bisq.android.util.DateUtil 21 | import org.assertj.core.api.Assertions.assertThat 22 | import org.junit.Assume.assumeFalse 23 | import org.junit.Assume.assumeTrue 24 | import org.junit.BeforeClass 25 | import org.junit.Test 26 | import java.util.Date 27 | import java.util.Locale 28 | import java.util.TimeZone 29 | 30 | class DateUtilTest { 31 | 32 | companion object { 33 | @BeforeClass 34 | @JvmStatic 35 | fun init() { 36 | TimeZone.setDefault(TimeZone.getTimeZone("UTC")) 37 | } 38 | } 39 | 40 | @Test 41 | fun testDefaultFormatReturnsFormattedString() { 42 | assertThat(DateUtil.format(1651974403000L)) 43 | .isEqualTo("2022-05-08 01:46:43") 44 | } 45 | 46 | @Test 47 | fun testFormatWithSpecifiedLocaleReturnsFormattedString() { 48 | assertThat(DateUtil.format(1651974403000L, Locale.GERMAN)) 49 | .isEqualTo("2022-05-08 01:46:43") 50 | } 51 | 52 | @Test 53 | fun testFormatWithSpecifiedPatternReturnsFormattedString() { 54 | assertThat(DateUtil.format(1651974403000L, pattern = "dd/MM/yyyy")) 55 | .isEqualTo("08/05/2022") 56 | } 57 | 58 | @Test 59 | fun testFormatWithSpecifiedTimezoneReturnsFormattedString() { 60 | val tz = TimeZone.getTimeZone("Pacific/Galapagos") // UTC-6 always 61 | assumeFalse("In daylight time", tz.inDaylightTime(Date())) 62 | assertThat(DateUtil.format(1651974403000L, timezone = tz)) 63 | .isEqualTo("2022-05-07 19:46:43") 64 | } 65 | 66 | @Test 67 | fun testFormatWithSpecifiedTimezoneDSTReturnsFormattedString() { 68 | val tz = TimeZone.getTimeZone("Europe/Helsinki") // UTC+3 when DST in effect 69 | assumeTrue("Not in daylight time", tz.inDaylightTime(Date())) 70 | assertThat(DateUtil.format(1651974403000L, timezone = tz)) 71 | .isEqualTo("2022-05-08 04:46:43") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_notification_table.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 19 | 20 | 21 | 29 | 30 | 42 | 43 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/services/NotificationReceiver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.services 19 | 20 | import android.content.BroadcastReceiver 21 | import android.content.Context 22 | import android.content.Intent 23 | import bisq.android.Logging 24 | import bisq.android.R 25 | import bisq.android.database.BisqNotification 26 | import bisq.android.ext.goAsync 27 | import bisq.android.model.Device 28 | 29 | class NotificationReceiver : BroadcastReceiver() { 30 | companion object { 31 | private const val TAG = "NotificationReceiver" 32 | } 33 | 34 | @Suppress("ReturnCount") 35 | override fun onReceive(context: Context, intent: Intent) { 36 | Logging().debug(TAG, "Notification received") 37 | 38 | if (Device.instance.key == null) { 39 | Logging().warn(TAG, "Ignoring received notification, device does not have a key") 40 | return 41 | } 42 | 43 | Logging().debug(TAG, "Processing notification") 44 | val bisqNotification: BisqNotification 45 | try { 46 | bisqNotification = NotificationProcessor.processNotification( 47 | intent.extras?.getString("encrypted").toString() 48 | ) 49 | } catch (e: ProcessingException) { 50 | e.message?.let { Logging().error(TAG, it) } 51 | Intent().also { broadcastIntent -> 52 | broadcastIntent.action = context.getString(R.string.intent_receiver_action) 53 | broadcastIntent.putExtra( 54 | "error", 55 | context.getString(R.string.failed_to_process_notification) 56 | ) 57 | context.sendBroadcast(broadcastIntent) 58 | } 59 | return 60 | } 61 | 62 | if (bisqNotification.type == null) { 63 | Logging().error(TAG, "Notification type is null: $bisqNotification") 64 | return 65 | } 66 | 67 | Logging().debug(TAG, "Handling ${bisqNotification.type} notification") 68 | goAsync { 69 | NotificationHandler.handleNotification(bisqNotification, context) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/test/java/bisq/android/tests/util/QrUtilTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.tests.util 19 | 20 | import android.graphics.Bitmap 21 | import android.graphics.BitmapFactory 22 | import bisq.android.util.QrUtil 23 | import com.google.zxing.WriterException 24 | import org.junit.Before 25 | import org.junit.Test 26 | import org.junit.runner.RunWith 27 | import org.mockito.ArgumentMatchers.any 28 | import org.mockito.ArgumentMatchers.anyInt 29 | import org.mockito.ArgumentMatchers.anyString 30 | import org.mockito.Mock 31 | import org.mockito.Mockito.`when` 32 | import org.powermock.api.mockito.PowerMockito.mockStatic 33 | import org.powermock.core.classloader.annotations.PrepareForTest 34 | import org.powermock.modules.junit4.PowerMockRunner 35 | 36 | @RunWith(PowerMockRunner::class) 37 | @PrepareForTest(BitmapFactory::class, Bitmap::class) 38 | class QrUtilTest { 39 | @Mock 40 | private val bitmap: Bitmap? = null 41 | 42 | @Before 43 | fun setup() { 44 | mockStatic(Bitmap::class.java) 45 | mockStatic(BitmapFactory::class.java) 46 | `when`(Bitmap.createBitmap(anyInt(), anyInt(), any())).thenReturn(bitmap) 47 | `when`(BitmapFactory.decodeFile(anyString())).thenReturn(bitmap) 48 | } 49 | 50 | @Test 51 | fun testCreateQrImageWithSimpleContent() { 52 | QrUtil.createQrImage("text") 53 | } 54 | 55 | @Test 56 | fun testCreateQrImageWithCustomDimensions() { 57 | QrUtil.createQrImage("text", 640, 640) 58 | } 59 | 60 | @Test(expected = IllegalArgumentException::class) 61 | fun testCreateQrImageWithEmptyContentsThrowsException() { 62 | QrUtil.createQrImage("") 63 | } 64 | 65 | @Test(expected = WriterException::class) 66 | fun testCreateQrImageWithTooLongContents() { 67 | val charPool: List = ('a'..'z') + ('A'..'Z') + ('0'..'9') 68 | val stringLength = 10000 69 | val randomString = 70 | (1000..stringLength) 71 | .map { kotlin.random.Random.nextInt(0, charPool.size) } 72 | .map(charPool::get) 73 | .joinToString("") 74 | QrUtil.createQrImage(randomString) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /docs/tests/notifications.feature: -------------------------------------------------------------------------------- 1 | Feature: Notifications 2 | Notifications are received via Firebase Cloud Messaging (FCM) from the Bisq client and shown 3 | within the mobile app. They can be triggered manually either by the Bisq client or the 4 | bisqremote tool. 5 | 6 | Background: A pairing token generated by the app needs to be shared with the Bisq client. 7 | Given the app has been paired with the Bisq client 8 | 9 | Scenario: Notifications are shown within the app when it is running in the foreground 10 | Given the app is running in the foreground 11 | When a notification is received 12 | Then the notification will be shown directly in the app and not in the system notification area 13 | 14 | Scenario: Notifications are shown in system notification area when the app is running in the background 15 | Given the app is running in the background 16 | When a notification is received 17 | Then the notification will appear in the system notification area 18 | And it will not appear in the app until clicked on 19 | 20 | Scenario: Notifications are shown in system notification area when the app is closed 21 | Given the app is closed 22 | When a notification is received 23 | Then the notification will appear in the system notification area 24 | And it will not appear in the app until clicked on 25 | 26 | Scenario: Notifications are shown if clicked on while the app is running in the foreground 27 | Given the app is running in the background or is closed 28 | When a notification is received 29 | And the app is opened 30 | And the notification is clicked on within the system notification area 31 | Then the notification will be shown within the app 32 | 33 | Scenario: Multiple notifications are shown in the system notification area 34 | Given the app is running in the background or is closed 35 | When multiple notifications are received 36 | Then each notification will appear in the system notification area 37 | 38 | Scenario: Notifications are shown in system notification area when the app is not paired and not running in the foreground 39 | Given the app has reset its pairing 40 | And the app is running in the background or is closed 41 | When a notification is received using the old pairing token 42 | Then the notification will appear in the system notification area 43 | And opening the notification will show a toast message indicating it failed to process the notification 44 | 45 | Scenario: Toast message is shown when receiving a notification if the app is not paired and running in the foreground 46 | Given the app has reset its pairing 47 | And the app is running in the foreground 48 | When a notification is received using the old pairing token 49 | Then a toast message is shown indicating it failed to process the notification 50 | -------------------------------------------------------------------------------- /app/src/test/java/bisq/android/tests/util/MaskingUtilTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.tests.util 19 | 20 | import bisq.android.util.MaskingUtil 21 | import org.assertj.core.api.Assertions.assertThat 22 | import org.junit.Test 23 | 24 | class MaskingUtilTest { 25 | @Test 26 | fun shouldMaskMiddleOfLongStringWithDefaultVisibleChars() { 27 | val result = MaskingUtil.maskSensitive("mySuperSecretPassword12345") 28 | assertThat(result).isEqualTo("mySup****************12345") 29 | } 30 | 31 | @Test 32 | fun shouldMaskMiddleOfStringWithCustomVisibleChars() { 33 | val result = MaskingUtil.maskSensitive("1234567890abcdef", 4) 34 | assertThat(result).isEqualTo("1234********cdef") 35 | } 36 | 37 | @Test 38 | fun shouldMaskMiddleOfStringWithCustomVisibleCharsAndCustomMaskCharacter() { 39 | val result = MaskingUtil.maskSensitive("VerySensitiveKeyHere", 6, '#') 40 | assertThat(result).isEqualTo("VerySe########eyHere") 41 | } 42 | 43 | @Test 44 | fun shouldMaskEntireStringIfShorterThanTwiceVisibleChars() { 45 | val result = MaskingUtil.maskSensitive("short", 3) 46 | assertThat(result).isEqualTo("*****") 47 | } 48 | 49 | @Test 50 | fun shouldMaskEntireStringIfShorterThanTwiceVisibleCharsWithCustomChar() { 51 | val result = MaskingUtil.maskSensitive("test", 3, '#') 52 | assertThat(result).isEqualTo("####") 53 | } 54 | 55 | @Test 56 | fun shouldReturnEmptyStringWhenInputIsNull() { 57 | val result = MaskingUtil.maskSensitive(null) 58 | assertThat(result).isEqualTo("") 59 | } 60 | 61 | @Test 62 | fun shouldReturnSameStringWhenInputIsEmpty() { 63 | val result = MaskingUtil.maskSensitive("") 64 | assertThat(result).isEqualTo("") 65 | } 66 | 67 | @Test 68 | fun shouldMaskEntireStringIfExactlyEqualToTwiceVisibleChars() { 69 | val result = MaskingUtil.maskSensitive("abcdefgh", 4) 70 | assertThat(result).isEqualTo("********") 71 | } 72 | 73 | @Test 74 | fun shouldHandleVeryLongStringWithLargeVisibleChars() { 75 | val result = MaskingUtil.maskSensitive("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 10) 76 | assertThat(result).isEqualTo("ABCDEFGHIJ****************1234567890") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/Logging.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android 19 | 20 | import android.util.Log 21 | import bisq.android.database.DebugLog 22 | import bisq.android.database.DebugLogLevel 23 | import bisq.android.database.DebugLogRepository 24 | import kotlinx.coroutines.CoroutineScope 25 | import kotlinx.coroutines.Dispatchers 26 | import kotlinx.coroutines.launch 27 | 28 | class Logging { 29 | companion object { 30 | private const val TAG = "Logging" 31 | } 32 | 33 | // Attempt to initialize debugRepository with the application context. 34 | // If the context is unavailable (e.g., in unit tests where Application is not initialized), 35 | // catch the exception and log a warning instead of throwing a NullPointerException. 36 | // This ensures that tests still work since trying to provide a mocked context is not straight forward. 37 | @Suppress("SwallowedException", "TooGenericExceptionCaught") 38 | private val debugRepository: DebugLogRepository? = try { 39 | val context = Application.applicationContext() 40 | DebugLogRepository(context) 41 | } catch (e: NullPointerException) { 42 | Log.w(TAG, "Skipping debugRepository initialization due to missing context") 43 | null 44 | } 45 | 46 | fun debug(tag: String, msg: String) { 47 | Log.d(tag, msg) 48 | insert(DebugLogLevel.DEBUG, msg) 49 | } 50 | 51 | fun info(tag: String, msg: String) { 52 | Log.i(tag, msg) 53 | insert(DebugLogLevel.INFO, msg) 54 | } 55 | 56 | fun warn(tag: String, msg: String) { 57 | Log.w(tag, msg) 58 | insert(DebugLogLevel.WARN, msg) 59 | } 60 | 61 | fun error(tag: String, msg: String) { 62 | Log.e(tag, msg) 63 | insert(DebugLogLevel.ERROR, msg) 64 | } 65 | 66 | private fun insert(level: DebugLogLevel, msg: String) { 67 | debugRepository?.let { 68 | CoroutineScope(Dispatchers.IO).launch { 69 | it.insert( 70 | DebugLog( 71 | timestamp = System.currentTimeMillis(), 72 | level = level, 73 | text = msg 74 | ) 75 | ) 76 | } 77 | } ?: Log.w(TAG, "Skipping log insert; debugRepository is unavailable") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 49 | 52 | 55 | 56 | 59 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/bisq/android/services/IntentReceiver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Bisq. 3 | * 4 | * Bisq is free software: you can redistribute it and/or modify it 5 | * under the terms of the GNU Affero General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or (at 7 | * your option) any later version. 8 | * 9 | * Bisq is distributed in the hope that it will be useful, but WITHOUT 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 12 | * License for more details. 13 | * 14 | * You should have received a copy of the GNU Affero General Public License 15 | * along with Bisq. If not, see . 16 | */ 17 | 18 | package bisq.android.services 19 | 20 | import android.app.Activity 21 | import android.content.BroadcastReceiver 22 | import android.content.Context 23 | import android.content.Intent 24 | import android.widget.Toast 25 | import bisq.android.Logging 26 | import bisq.android.R 27 | import bisq.android.model.Device 28 | import bisq.android.model.DeviceStatus 29 | import bisq.android.model.NotificationType 30 | import bisq.android.ui.PairedBaseActivity 31 | import bisq.android.ui.UnpairedBaseActivity 32 | 33 | class IntentReceiver(private val activity: Activity? = null) : BroadcastReceiver() { 34 | companion object { 35 | private const val TAG = "IntentReceiver" 36 | } 37 | 38 | @Suppress("ReturnCount") 39 | override fun onReceive(context: Context, intent: Intent) { 40 | Logging().debug(TAG, "Intent received") 41 | 42 | if (intent.action == null || 43 | !intent.action.equals(context.getString(R.string.intent_receiver_action)) 44 | ) { 45 | Logging().debug( 46 | TAG, 47 | "Ignoring intent, action is not " + context.getString(R.string.intent_receiver_action) 48 | ) 49 | return 50 | } 51 | 52 | if (intent.hasExtra("error")) { 53 | val errorMessage = intent.getStringExtra("error") 54 | Logging().error(TAG, errorMessage!!) 55 | Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show() 56 | return 57 | } 58 | 59 | if (!intent.hasExtra("type")) { 60 | Logging().debug(TAG, "Ignoring intent, missing notification type") 61 | return 62 | } 63 | 64 | val type = intent.getStringExtra("type") 65 | if (type == NotificationType.SETUP_CONFIRMATION.name && activity is UnpairedBaseActivity) { 66 | Logging().debug(TAG, "Pairing confirmed") 67 | activity.pairingConfirmed() 68 | } else if (type == NotificationType.ERASE.name && activity is PairedBaseActivity) { 69 | Logging().debug(TAG, "Pairing removed") 70 | Device.instance.status = DeviceStatus.UNPAIRED 71 | activity.pairingRemoved(context.getString(R.string.pairing_erased)) 72 | } else { 73 | Logging().debug(TAG, "Ignoring $type notification") 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_pairing_send.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 19 | 20 | 21 | 29 | 30 | 44 | 45 |