diff --git a/.circleci/config.yml b/.circleci/config.yml index ca18843c..ae0d3da8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,12 +70,25 @@ jobs: tag: 2024.07.1-ndk resource_class: large environment: - GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=2" + # Lowered from -Xmx4g/workers.max=2: the Gradle daemon heap plus forked + # unit-test worker JVMs plus OS/container overhead were exceeding the + # `large` resource class's available RAM, causing the daemon to be + # OOM-killed mid-build ("daemon has disappeared"). Reducing the daemon + # heap and worker parallelism leaves more headroom for the AGP-forked + # test JVMs without changing the resource class. + GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx3g -XX:MaxMetaspaceSize=512m -Dorg.gradle.daemon=false -Dorg.gradle.workers.max=1" steps: - setup_environment - run: name: "Execute Unit Tests" - command: ./gradlew testBrainwalletDebugUnitTest --no-daemon --max-workers=2 + # -x detekt: android-build-logic's DetektSetup.attachDetektTask() wires + # `detekt` (autoCorrect=true, parallel=true, HTML/XML/TXT/SARIF/MD reports) + # as a dependency of every compile*/assemble* task project-wide, so a plain + # test run was also paying for 3-4 full static-analysis passes it doesn't + # need โ€” real CPU/memory competing with compilation and the forked test + # JVMs on this resource-constrained executor. Excluding it here only + # affects this CI job, not local dev/lint workflows. + command: ./gradlew testBrainwalletDebugUnitTest --no-daemon --max-workers=1 -x detekt - android/save_gradle_cache - run: name: Save test results diff --git a/.github/workflows/pr-summary-copilot.yml b/.github/workflows/pr-summary-copilot.yml index cd3c3beb..92bec6f7 100644 --- a/.github/workflows/pr-summary-copilot.yml +++ b/.github/workflows/pr-summary-copilot.yml @@ -1,11 +1,6 @@ name: ๐Ÿค– Copilot PR Summary on: - pull_request: - types: [opened, reopened] - branches: - - develop - - main workflow_dispatch: permissions: diff --git a/README.md b/README.md index c92a651c..ce2ed228 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,80 @@ - # Brainwallet: Android -The open source code of Brainwallet Android -## CI/CD Status -**main**: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/gruntsoftware/android/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/gruntsoftware/android/tree/main) +**Brainwallet** is a free, open-source, self-custodial [Litecoin](https://litecoin.org) wallet for Android. Your seed phrase and keys stay on your device โ€” Brainwallet never has custody of your funds. + +[![CircleCI](https://dl.circleci.com/status-badge/img/gh/gruntsoftware/android/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/gruntsoftware/android/tree/main) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Website](https://img.shields.io/badge/website-brainwallet.co-blue)](https://brainwallet.co) + +**CI status** โ€” [main](https://dl.circleci.com/status-badge/redirect/gh/gruntsoftware/android/tree/main) ยท [develop](https://dl.circleci.com/status-badge/redirect/gh/gruntsoftware/android/tree/develop) + +## Download + +- **Play Store**: [ltd.grunt.brainwallet](https://play.google.com/store/apps/details?id=ltd.grunt.brainwallet) +- **iOS**: [gruntsoftware/ios](https://github.com/gruntsoftware/ios) +- **Website**: [brainwallet.co](https://brainwallet.co) +- **Support**: [brainwallet.co/support](https://www.brainwallet.co/support) + +## Features + +- Self-custodial Litecoin wallet โ€” your seed phrase never leaves your device +- Send/receive LTC with real-time fee estimation and fiat conversion +- Games to help you memorize your seed phrase (Check out Fallinmoji!) +- PIN app lock +- Buy/sell LTC and gift cards via in-app widgets +- Modern Jetpack Compose UI, with a legacy Java/UIKit-era codebase still being progressively migrated + +## Auditing code + +### Prerequisites +- Android Studio (current stable) with SDK 36 installed, NDK `25.1.8937393`, CMake `3.22.1` +- `minSdk 29`, `targetSdk 35` + +## Architecture + +- **Language/UI**: Kotlin, Jetpack Compose (legacy screens still in Java/`View`-based UIKit-era code under `presenter/`, being migrated incrementally) +- **DI**: [Koin](https://insert-koin.io/) +- **Concurrency**: Kotlin Coroutines & `StateFlow` +- **Wallet core**: native C/C++ (`app/src/main/jni/core`), elliptic curve crypto via [`secp256k1`](https://github.com/bitcoin-core/secp256k1) +- **Modules**: `app` (main app), `bw-gdlib` (mini-games, LibGDX, composite build), `modules/private-general-purpose` (in-app purchases + general-purpose features), `android-build-logic` (shared Gradle convention plugins) + +## Testing + +Unit tests live under `app/src/test/{java,kotlin}`. Run them with `./gradlew test`, or a specific variant with `./gradlew testBrainwalletDebugUnitTest`. CI runs on CircleCI (`.circleci/config.yml`). -**develop**: [![CircleCI](https://dl.circleci.com/status-badge/img/gh/gruntsoftware/android/tree/develop.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/gruntsoftware/android/tree/develop) +## Security +Found a security vulnerability? Please **do not** open a public issue โ€” see [SECURITY.md](SECURITY.md) for how to report it privately. + + +## License + +Brainwallet Android is released under the [MIT License](LICENSE). + +--- + +## Release Notes + +For the full, up-to-date changelog see [GitHub Releases](https://github.com/gruntsoftware/android/releases) and the [compare view](https://github.com/gruntsoftware/android/compare). Highlights from recent versions: -# ๐Ÿš€ Brainwallet Android โ€” Release Notes ### v4.9.0 -### โœจ New Features +#### โœจ New Features -#### ๐Ÿ“‹ Copy Transaction Details to Clipboard +##### ๐Ÿ“‹ Copy Transaction Details to Clipboard Transaction details can now be copied directly to the clipboard from the transaction detail view. A new `BRClipboardManager.putDetailsClipboard()` method handles the copy action with full analytics tracking, making it easy to share or reference transaction information outside the app. -#### ๐Ÿ“Š Analytics Tracking for Key Interactions +##### ๐Ÿ“Š Analytics Tracking for Key Interactions Firebase analytics events are now fired for core user interactions โ€” fiat/LTC toggle, balance visibility toggle, and transaction detail copy โ€” giving the team better insight into how users engage with the wallet's main screens. --- -### ๐ŸŽจ UI / Design System +#### ๐ŸŽจ UI / Design System -#### ๐Ÿงฑ Unified `bentoSurface` Design System -A new `bentoSurface()` modifier in `BentoModifiers.kt` consolidates gradient and border styling across the entire bento UI. Previously hardcoded values like `1.dp` borders are now driven by shared constants (`bentoBorderWidth`, `bentoCornerRadius`, `bentoCornerRadius`, `bentoSpacer`, `transactionActionHt`, `transactionDetailHt`), making the visual system consistent and easy to maintain going forward. +##### ๐Ÿงฑ Unified `bentoSurface` Design System +A new `bentoSurface()` modifier in `BentoModifiers.kt` consolidates gradient and border styling across the entire bento UI. Previously hardcoded values like `1.dp` borders are now driven by shared constants (`bentoBorderWidth`, `bentoCornerRadius`, `bentoSpacer`, `transactionActionHt`, `transactionDetailHt`), making the visual system consistent and easy to maintain going forward. -#### ๐Ÿƒ Bento Section Refactors +##### ๐Ÿƒ Bento Section Refactors - **GameHub** โ€” migrated from `Card` to `Box` with `bentoSurface()`, with a `clickable` modifier added - **Favourites** โ€” refactored to `bentoSurface()` with improved `Spacer` layout - **LTC Picker** โ€” label repositioned to top, spacing and item heights adjusted @@ -35,66 +83,58 @@ A new `bentoSurface()` modifier in `BentoModifiers.kt` consolidates gradient and --- -### ๐Ÿ”ง Technical Changes +#### ๐Ÿ”ง Technical Changes - `MainScreen`, `MainScreenEvent`, and `MainViewModel` updated for new event handling and state management - `NoWifiBalanceAlertScreen` and `ReceiveDialog` border widths replaced with `bentoBorderWidth` constant - `strings.xml` updated with new/updated string resources - `gradle/libs.versions.toml` and `app/build.gradle.kts` dependencies updated - `detekt-app-baseline.xml` baseline updated -- Version bumped: **v4.8.4 (202506314) โ†’ v4.9.0 (202506315)** +- Version bumped: **v4.8.4 (202506314) โ†’ v4.9.0 (202506315)** - Release/v4.7.2 202506296 by @kcw-grunt in https://github.com/gruntsoftware/android/pull/134 - Release/v4.8.0 by @kcw-grunt in https://github.com/gruntsoftware/android/pull/145 **Full Changelog**: https://github.com/gruntsoftware/android/compare/v4.7.2...v4.9.0 + ### v4.8.3 -## โœจ New UI โ€” Favourites & Tutorials Bento Sections +#### โœจ New UI โ€” Favourites & Tutorials Bento Sections **PR [#191](https://github.com/gruntsoftware/android/pull/191)** The main screen now features two new purpose-built bento panels replacing the generic placeholder container. The **Favourites** panel displays a set of coloured circular indicators using the app's design theme colours (affirm, info, warn, error) with full dark/light mode support. The **Tutorials** panel shows a "Coming Soon" placeholder consistent with the broader bento design language. The old `HomeBentoContainer` has been removed. String resources for both sections have been translated across 19 locales including Arabic, Chinese (Traditional & Simplified), French, German, Farsi, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Punjabi, Brazilian Portuguese, Russian, Spanish, Swedish, Turkish, and Ukrainian. -## ๐Ÿ”ง Send Screen Overhaul & Financial Precision +#### ๐Ÿ”ง Send Screen Overhaul & Financial Precision **PR [#182](https://github.com/gruntsoftware/android/pull/182)** The send transaction flow has been fully redesigned as a multi-step paged screen (Pre-send โ†’ Confirm โ†’ Authenticate). All monetary values have been migrated from `Float` to `BigDecimal` with explicit rounding modes, eliminating floating-point precision issues in fiat/LTC conversions. Transaction fees are now calculated dynamically using `getFeePerKb()` rather than a static default. The pre-send composable has been broken into focused sub-components (`PreSendAddressRow`, `PreSendAmountRow`, `PreSendMemoRow`), and passcode UI components have been extracted into a dedicated package. QR code scanning is now wired into the send flow via an `EventBus` event. A `BWSender` class handles transaction preparation with proper error handling and activity context management. -## ๐Ÿงช Test Coverage Expanded to 309 Tests +#### ๐Ÿงช Test Coverage Expanded to 309 Tests **PR [#182](https://github.com/gruntsoftware/android/pull/182)** - Unit test coverage has been significantly expanded alongside the send screen work, bringing the total suite to **309 tests**. New test classes include `AppModuleTest`, `SendStateTest`, and `BrainwalletAppTest`, covering the Koin DI module wiring, send state logic, and core app initialisation paths. - - -### v3.9.0 - v3.9.1 Latest -Update README for improved description by @kcw-grunt in #78 -Beta Release [ ๐Ÿš€ ] Merge Develop into Main by @kcw-grunt in #81 - -#### Fixes and Changes: +### v3.9.0 โ€“ v3.9.1 +- Update README for improved description by @kcw-grunt in #78 +- Beta Release [ ๐Ÿš€ ] Merge Develop into Main by @kcw-grunt in #81 - Current fiat preference from Settings needs to be reset if set in the TickerBento - Localizations are covered to 100% - Mini game FALLINMOJI is present in the Welcome and Game Hub -- When setting the theme from the Settings and the Lock Screen and the Main screen is not consistently applied -- In general the fonts in the app are not consistent and need to be managed properly for consistency -- Mini game sounds set a nominal level -- Layout for iPhone 8 - iPhone 17 Pro Max is set for: Welcome Screen -- Support.brainwallet.co link is fixed - -### v3.6.0 Latest - -#### What's Changed -๐Ÿš€[Release v3.5.0] Merge into Main by @kcw-grunt in #51 -Full Changelog: v3.4.2...v3.6.0\ - -#### Updates: -- using bundle exec fastlane single_unit_test_all -- downgrade firebase to 11.12.0 -- polsih ci config +- Theme applied from Settings/Lock Screen/Main screen made consistent +- Fonts made consistent across the app +- Mini game sounds set to a nominal level +- Layout fixes for iPhone 8 โ€“ iPhone 17 Pro Max on the Welcome Screen +- Support.brainwallet.co link fixed + +### v3.6.0 +- ๐Ÿš€ [Release v3.5.0] merged into Main by @kcw-grunt in #51 +- Full Changelog: v3.4.2...v3.6.0 +- Switched to `bundle exec fastlane single_unit_test_all` +- Downgraded Firebase to 11.12.0 +- CI config polish - ๐Ÿฆพ Chore/migrate ready onboarding - Fix/login view crash -- ๐Ÿงฐ fix: Removed the thread blocking seen in the lock screen trx. loading +- ๐Ÿงฐ Fix: removed thread blocking in the lock screen transaction loading - Epic/settings migration (#50) -- Chore/refactor firebase analytics +- Chore/refactor Firebase analytics - Chore/activate test coverage ### v3.3.1 - Added locale filter -- Made improvements to UI -- Fixes +- UI improvements +- Various fixes diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..dd25a622 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,68 @@ +# Security Policy + +Brainwallet is a self-custodial Litecoin wallet. A vulnerability here can put +users' funds or seed phrases at direct risk, so we treat security reports +with priority and ask that they be disclosed to us privately first. + +## Reporting a Vulnerability + +**Please do not open a public GitHub issue for security vulnerabilities.** +Public issues are indexed and searchable, and a wallet vulnerability +disclosed that way can be exploited before we're able to ship a fix. + +To report a vulnerability, contact us at [brainwallet.co/support](https://www.brainwallet.co/support) +and clearly mark your message as a **security report**. + +When reporting, please include: +- A description of the vulnerability and its potential impact +- Steps to reproduce, or a proof of concept if you have one +- The affected version (or commit hash) and platform (Android/iOS) +- Any suggested severity or CVSS score, if you have one + +**What to expect:** +- We aim to acknowledge new reports within a few business days. +- We'll work with you to confirm the issue, keep you updated as we + investigate and remediate it, and let you know once a fix has shipped. +- With your permission, we're happy to credit you for the report once it's + resolved. + +## Scope + +**In scope:** +- The Brainwallet Android app in this repository โ€” key management, + transaction signing and broadcast, wallet backup/recovery (seed phrase), + PIN/biometric authentication, and secure local storage + (`app/src/main/jni/core`, `app/src/main/secp/secp256k1`) +- Companion modules pulled in as git submodules: `bw-gdlib`, + `android-private-general-purpose`, `android-build-logic` + +**Out of scope:** +- Vulnerabilities in third-party dependencies with their own upstream + disclosure process โ€” please report to the upstream project (we'd + appreciate a copy of the report too, but it isn't required) +- Social engineering, phishing, or attacks requiring physical access to an + unlocked device +- Issues that only reproduce on a rooted/jailbroken device or a device + already compromised by other malware +- Denial-of-service against our backend/infrastructure rather than the app + itself +- Missing security best-practices with no demonstrated, concrete impact + +## Supported Versions + +Brainwallet ships continuously through the Play Store, and we only support +the most recently published release โ€” please make sure you can reproduce an +issue on the latest version before reporting it. + +| Version | Supported | +| -------------- | :-------: | +| Latest release | โœ… | +| Older releases | โŒ | + +## Safe Harbor + +We consider security research conducted in good faith and in line with this +policy to be authorized. We will not pursue legal action against researchers +who make a genuine effort to avoid privacy violations, data destruction, and +service disruption, and who report vulnerabilities to us privately as +described above before any public disclosure. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9b590970..f52729ee 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,8 +31,8 @@ android { applicationId = "ltd.grunt.brainwallet" minSdk = 29 targetSdk = 35 - versionCode = 202506342 - versionName = "v4.10.4" + versionCode = 202506345 + versionName = "v4.10.6" multiDexEnabled = true base.archivesName.set("${defaultConfig.versionName}(${defaultConfig.versionCode})") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -172,6 +172,7 @@ dependencies { implementation(project(":iap")) implementation(project(":core")) implementation("androidx.webkit:webkit:1.9.0") + implementation(libs.keepsafe.relinker) implementation(libs.androidx.benchmark.traceprocessor) implementation(libs.androidx.ui.graphics) testImplementation(testFixtures(project(":app"))) diff --git a/app/src/main/java/com/brainwallet/BrainwalletApp.kt b/app/src/main/java/com/brainwallet/BrainwalletApp.kt index 38ba6de7..36cba83f 100644 --- a/app/src/main/java/com/brainwallet/BrainwalletApp.kt +++ b/app/src/main/java/com/brainwallet/BrainwalletApp.kt @@ -13,6 +13,7 @@ import com.brainwallet.tools.listeners.SyncReceiver import com.brainwallet.tools.manager.AnalyticsManager import com.brainwallet.tools.util.Utils import com.brainwallet.constants.BWConstants +import com.getkeepsafe.relinker.ReLinker import com.google.firebase.crashlytics.FirebaseCrashlytics import org.koin.android.ext.android.inject import timber.log.Timber @@ -27,6 +28,7 @@ open class BrainwalletApp : Application() { override fun onCreate() { super.onCreate() + ReLinker.loadLibrary(this, BWConstants.NATIVE_LIB_NAME) val enableCrashlytics = !Utils.isEmulatorOrDebug(this) notificationHandler.setupNotificationChannels(this) diff --git a/app/src/main/java/com/brainwallet/presenter/activities/BreadActivity.java b/app/src/main/java/com/brainwallet/presenter/activities/BreadActivity.java index 29c94850..ae947471 100644 --- a/app/src/main/java/com/brainwallet/presenter/activities/BreadActivity.java +++ b/app/src/main/java/com/brainwallet/presenter/activities/BreadActivity.java @@ -14,8 +14,6 @@ import android.os.Handler; import android.view.ViewTreeObserver; import android.view.animation.AnimationUtils; -import android.widget.Button; -import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import android.widget.ViewFlipper; @@ -27,7 +25,6 @@ import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; import androidx.core.app.ActivityCompat; -import androidx.drawerlayout.widget.DrawerLayout; import androidx.transition.ChangeBounds; import androidx.transition.Fade; import androidx.transition.TransitionManager; @@ -40,7 +37,6 @@ import com.brainwallet.presenter.activities.util.BRActivity; import com.brainwallet.presenter.customviews.BRNotificationBar; import com.brainwallet.presenter.history.HistoryFragment; -import com.brainwallet.tools.animation.BRAnimator; import com.brainwallet.tools.animation.TextSizeTransition; import com.brainwallet.tools.manager.AnalyticsManager; import com.brainwallet.tools.manager.BRSharedPrefs; @@ -63,7 +59,6 @@ import com.brainwallet.wallet.BRPeerManager; import com.brainwallet.wallet.BRWalletManager; import com.google.android.gms.tasks.Task; -import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.navigation.NavigationView; import com.google.android.play.core.review.ReviewInfo; import com.google.android.play.core.review.ReviewManager; @@ -77,15 +72,9 @@ public class BreadActivity extends BRActivity implements BRWalletManager.OnBalan TransactionDataSource.OnTxAddedListener, InternetManager.ConnectionReceiverListener { public static final Point screenParametersPoint = new Point(); - private static final float PRIMARY_TEXT_SIZE = 24f; - private static final float SECONDARY_TEXT_SIZE = 12.8f; - private int mSelectedBottomNavItem = -1; private InternetManager mConnectionReceiver; - private Button primaryPrice; - private Button secondaryPrice; private TextView equals; - private ImageButton menuBut; private TextView balanceTxtV; public static boolean appVisible = false; @@ -94,11 +83,9 @@ public class BreadActivity extends BRActivity implements BRWalletManager.OnBalan private boolean uiIsDone; private static BreadActivity app; - private BottomNavigationView bottomNav; private Handler mHandler = new Handler(); private NavigationView navigationDrawer; - private DrawerLayout drawerLayout; private HomeSettingDrawerComposeView homeSettingDrawerComposeView; public static BreadActivity getApp() { @@ -115,12 +102,7 @@ protected void onCreate(Bundle savedInstanceState) { app = this; getWindowManager().getDefaultDisplay().getSize(screenParametersPoint); - setListeners(); - - primaryPrice.setTextSize(PRIMARY_TEXT_SIZE); - secondaryPrice.setTextSize(SECONDARY_TEXT_SIZE); onConnectionChanged(InternetManager.getInstance().isConnected(this)); - bottomNav.setSelectedItemId(R.id.nav_history); } private void addObservers() { @@ -147,25 +129,6 @@ protected void onNewIntent(Intent intent) { setUrlHandler(intent); } - private void setListeners() { - bottomNav.setOnNavigationItemSelectedListener(item -> handleNavigationItemSelected(item.getItemId())); - - menuBut.setOnClickListener(v -> { - if (BRAnimator.isClickAllowed()) { - drawerLayout.open(); - } - }); - } - - public boolean handleNavigationItemSelected(int menuItemId) { - if (mSelectedBottomNavItem == menuItemId) return true; - mSelectedBottomNavItem = menuItemId; - //TODO: Removal as Compose Send workflow is live and in Production - // Will remove as cruft as a backlog item - - return true; - } - @Override protected void onRestart() { super.onRestart(); diff --git a/app/src/main/java/com/brainwallet/presenter/activities/util/BRActivity.java b/app/src/main/java/com/brainwallet/presenter/activities/util/BRActivity.java index 8f3e93f1..7f02f5cc 100644 --- a/app/src/main/java/com/brainwallet/presenter/activities/util/BRActivity.java +++ b/app/src/main/java/com/brainwallet/presenter/activities/util/BRActivity.java @@ -33,10 +33,6 @@ /// TBD public class BRActivity extends AppCompatActivity { - static { - System.loadLibrary(BWConstants.NATIVE_LIB_NAME); - } - private SettingRepository settingRepository = (SettingRepository) KoinJavaComponent.inject(SettingRepository.class).getValue(); @Override diff --git a/app/src/main/java/com/brainwallet/tools/listeners/SyncReceiver.java b/app/src/main/java/com/brainwallet/tools/listeners/SyncReceiver.java index 92383e93..ef496cc2 100644 --- a/app/src/main/java/com/brainwallet/tools/listeners/SyncReceiver.java +++ b/app/src/main/java/com/brainwallet/tools/listeners/SyncReceiver.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.Intent; -import com.brainwallet.constants.BWConstants; import com.brainwallet.wallet.BRWalletManager; public class SyncReceiver extends IntentService { @@ -12,11 +11,6 @@ public class SyncReceiver extends IntentService { public final static String SYNC_RECEIVER = "SYNC_RECEIVER"; public static Context app; - // private Calendar c = Calendar.getInstance(); - static { - System.loadLibrary(BWConstants.NATIVE_LIB_NAME); - } - public SyncReceiver() { super("SyncReceiver"); } diff --git a/app/src/main/java/com/brainwallet/ui/bentosections/shopbento/ShopBentoViewModel.kt b/app/src/main/java/com/brainwallet/ui/bentosections/shopbento/ShopBentoViewModel.kt index 9c9f2d59..a2576156 100644 --- a/app/src/main/java/com/brainwallet/ui/bentosections/shopbento/ShopBentoViewModel.kt +++ b/app/src/main/java/com/brainwallet/ui/bentosections/shopbento/ShopBentoViewModel.kt @@ -6,6 +6,7 @@ import com.brainwallet.ui.BrainwalletViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.koin.android.annotation.KoinViewModel @@ -26,41 +27,37 @@ class ShopBentoViewModel( val currentCountryISO: String = Locale.getDefault().country.ifEmpty { "US" } init { - viewModelScope.launch { - settingRepository.settings.collect { setting -> - _state.update { - it.copy( - darkMode = setting.isDarkMode, - countryIso = currentCountryISO - ) - } - } - } viewModelScope.launch { shopProxyRepository.refresh() - shopProxyRepository.shopProxy.collect { shopList -> - val widget = shopList.firstOrNull()?.widget.orEmpty() - val cards = shopList.firstOrNull()?.shopCards.orEmpty() - .filter { it.countryCode == currentCountryISO } - var imageUrl1 = "" - var imageUrl2 = "" - var imageUrl3 = "" + combine( + settingRepository.settings, + shopProxyRepository.shopProxy + ) { setting, shopList -> setting to shopList } + .collect { (setting, shopList) -> + val widget = shopList.firstOrNull()?.widget.orEmpty() + val cards = shopList.firstOrNull()?.shopCards.orEmpty() + .filter { it.countryCode == currentCountryISO } + var imageUrl1 = "" + var imageUrl2 = "" + var imageUrl3 = "" - if (cards.count() >= 3) { - imageUrl1 = cards[0].cardImageWebP - imageUrl2 = cards[1].cardImageWebP - imageUrl3 = cards[2].cardImageWebP - } - _state.update { - it.copy( - shopBaseUrl = widget, - shopCards = cards, - cardImageURL1 = imageUrl1, - cardImageURL2 = imageUrl2, - cardImageURL3 = imageUrl3 - ) + if (cards.count() >= 3) { + imageUrl1 = cards[0].cardImageWebP + imageUrl2 = cards[1].cardImageWebP + imageUrl3 = cards[2].cardImageWebP + } + _state.update { + it.copy( + darkMode = setting.isDarkMode, + countryIso = currentCountryISO, + shopBaseUrl = widget, + shopCards = cards, + cardImageURL1 = imageUrl1, + cardImageURL2 = imageUrl2, + cardImageURL3 = imageUrl3 + ) + } } - } } } diff --git a/app/src/main/res/layout/activity_bread.xml b/app/src/main/res/layout/activity_bread.xml index 1a22edc4..282801de 100644 --- a/app/src/main/res/layout/activity_bread.xml +++ b/app/src/main/res/layout/activity_bread.xml @@ -2,7 +2,6 @@ @@ -71,23 +70,9 @@ android:text="@string/ManageWallet.balance" android:textColor="@color/white" android:textSize="18sp" - app:layout_constraintBottom_toTopOf="@+id/primary_price" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.0" /> - -