diff --git a/app/src/main/java/to/bitkit/env/Env.kt b/app/src/main/java/to/bitkit/env/Env.kt index 3c7e2e684..c5fed1199 100644 --- a/app/src/main/java/to/bitkit/env/Env.kt +++ b/app/src/main/java/to/bitkit/env/Env.kt @@ -121,6 +121,12 @@ internal object Env { return storagePathOf(walletIndex, network.name.lowercase(), "core") } + fun buildBitrefillUri( + page: String + ): String { + return "$BIT_REFILL_URL/$page/$BITREFILL_PARAMS" + } + private fun storagePathOf(walletIndex: Int, network: String, dir: String): String { require(::appStoragePath.isInitialized) { "App storage path should be 'context.filesDir.absolutePath'." } val path = Path(appStoragePath, network, "wallet$walletIndex", dir) @@ -174,7 +180,11 @@ internal object Env { const val APP_STORE_URL = "https://apps.apple.com/app/bitkit-wallet/id6502440655" const val PLAY_STORE_URL = "https://play.google.com/store/apps/details?id=to.bitkit" const val EXCHANGES_URL = "https://bitcoin.org/en/exchanges#international" - const val BIT_REFILL_URL = "https://www.bitrefill.com/br/en/gift-cards/" + const val BIT_REFILL_URL = "https://embed.bitrefill.com" + private const val BITREFILL_REF = "AL6dyZYt" + private const val BITREFILL_PAYMENT_METHOD = "bitcoin" // Payment method "bitcoin" gives a unified invoice + private const val BITREFILL_APP_NAME = "Bitkit" + private const val BITREFILL_PARAMS = "?ref=${BITREFILL_REF}&paymentMethod=${BITREFILL_PAYMENT_METHOD}&theme=dark&utm_source=${BITREFILL_APP_NAME}" const val BITKIT_WEBSITE = "https://bitkit.to/" const val SYNONYM_CONTACT = "https://synonym.to/contact" const val SYNONYM_MEDIUM = "https://medium.com/synonym-to" diff --git a/app/src/main/java/to/bitkit/models/BitrefillCategory.kt b/app/src/main/java/to/bitkit/models/BitrefillCategory.kt new file mode 100644 index 000000000..b1007ad89 --- /dev/null +++ b/app/src/main/java/to/bitkit/models/BitrefillCategory.kt @@ -0,0 +1,60 @@ +package to.bitkit.models + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CardGiftcard +import androidx.compose.material.icons.filled.Checkroom +import androidx.compose.material.icons.filled.DeliveryDining +import androidx.compose.material.icons.filled.DirectionsBoat +import androidx.compose.material.icons.filled.DirectionsCar +import androidx.compose.material.icons.filled.FavoriteBorder +import androidx.compose.material.icons.filled.Flight +import androidx.compose.material.icons.filled.Headphones +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Layers +import androidx.compose.material.icons.filled.Pets +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material.icons.filled.Print +import androidx.compose.material.icons.filled.Public +import androidx.compose.material.icons.filled.Restaurant +import androidx.compose.material.icons.filled.ShoppingBag +import androidx.compose.material.icons.filled.ShoppingCart +import androidx.compose.material.icons.filled.Storefront +import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material.icons.filled.VideogameAsset +import androidx.compose.ui.graphics.vector.ImageVector + +/** + * Represents the categories for purchases. + * + * @property title The display name of the category. + * @property route The navigation route associated with the category. + * @property icon The visual icon for the category. + */ +enum class BitrefillCategory( + val title: String, + val route: String, + val icon: ImageVector +) { + APPAREL("Apparel", "buy/apparel", Icons.Filled.Checkroom), + AUTOMOBILES("Automobiles", "buy/automobiles", Icons.Filled.DirectionsCar), + CRUISES("Cruises", "buy/cruises", Icons.Filled.DirectionsBoat), + ECOMMERCE("Ecommerce", "buy/ecommerce", Icons.Filled.ShoppingCart), + ELECTRONICS("Electronics", "buy/electronics", Icons.Filled.Print), + ENTERTAINMENT("Entertainment", "buy/entertainment", Icons.Filled.Headphones), + EXPERIENCES("Experiences", "buy/experiences", Icons.Filled.Public), + FLIGHTS("Flights", "buy/flights", Icons.Filled.Flight), + FOOD("Food", "buy/food", Icons.Filled.Storefront), + FOOD_DELIVERY("Food Delivery", "buy/food-delivery", Icons.Filled.DeliveryDining), + GAMES("Games", "buy/games", Icons.Filled.VideogameAsset), + GIFTS("Gifts", "buy/gifts", Icons.Filled.CardGiftcard), + GROCERIES("Groceries", "buy/groceries", Icons.Filled.ShoppingBag), + HEALTH_AND_BEAUTY("Health & Beauty", "buy/health-beauty", Icons.Filled.FavoriteBorder), + HOME("Home", "buy/home", Icons.Filled.Home), + MULTI_BRAND("Multi-Brand", "buy/multi-brand", Icons.Filled.Layers), + PETS("Pets", "buy/pets", Icons.Filled.Pets), + RESTAURANTS("Restaurants", "buy/restaurants", Icons.Filled.Restaurant), + RETAIL("Retail", "buy/retail", Icons.Filled.Storefront), + STREAMING("Streaming", "buy/streaming", Icons.Filled.Videocam), + TRAVEL("Travel", "buy/travel", Icons.Filled.Flight), + VOIP("VoIP", "buy/voip", Icons.Filled.Phone) +} diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 20d6d0f18..8e5e8ff31 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -47,6 +47,7 @@ import to.bitkit.ui.screens.scanner.QrScanningScreen import to.bitkit.ui.screens.scanner.SCAN_REQUEST_KEY import to.bitkit.ui.screens.shop.ShopDiscoverScreen import to.bitkit.ui.screens.shop.ShopIntroScreen +import to.bitkit.ui.screens.shop.ShopWebViewScreen import to.bitkit.ui.screens.transfer.FundingAdvancedScreen import to.bitkit.ui.screens.transfer.FundingScreen import to.bitkit.ui.screens.transfer.LiquidityScreen @@ -405,7 +406,7 @@ private fun RootNavHost( home(walletViewModel, appViewModel, activityListViewModel, settingsViewModel, navController) settings(navController, settingsViewModel) profile(navController, settingsViewModel) - shop(navController, settingsViewModel) + shop(navController, settingsViewModel, appViewModel) generalSettings(navController) advancedSettings(navController) aboutSettings(navController) @@ -706,6 +707,7 @@ private fun NavGraphBuilder.profile( private fun NavGraphBuilder.shop( navController: NavHostController, settingsViewModel: SettingsViewModel, + appViewModel: AppViewModel, ) { composableWithDefaultTransitions { ShopIntroScreen( @@ -719,6 +721,21 @@ private fun NavGraphBuilder.shop( composableWithDefaultTransitions { ShopDiscoverScreen( onClose = { navController.navigateToHome() }, + onBack = { navController.popBackStack() }, + navigateWebView = { page, title -> + navController.navigate(Routes.ShopWebView(page = page, title = title)) + } + ) + } + composableWithDefaultTransitions { navBackEntry -> + ShopWebViewScreen ( + onClose = { navController.navigateToHome() }, + onBack = { navController.popBackStack() }, + page = navBackEntry.toRoute().page, + title = navBackEntry.toRoute().title, + onPaymentIntent = { data -> + appViewModel.onScanSuccess(data) + } ) } } @@ -1589,6 +1606,9 @@ sealed interface Routes { @Serializable data object ShopDiscover : Routes + @Serializable + data class ShopWebView(val page: String, val title: String) : Routes + @Serializable data object WidgetsIntro : Routes diff --git a/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt b/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt index 385b2cb85..0abe08e5b 100644 --- a/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt +++ b/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString @@ -34,12 +35,14 @@ fun SuggestionCard( title: String, description: String, @DrawableRes icon: Int, - onClose: () -> Unit, + onClose: (() -> Unit)? = null, + size: Int = 152, + captionColor: Color = Colors.White64, onClick: () -> Unit, ) { Box( modifier = modifier - .size(152.dp) + .size(size.dp) .clip(ShapeDefaults.Large) .gradientBackground(gradientColor.copy(alpha = 0.30f)) .clickableAlpha { onClick() } @@ -58,18 +61,21 @@ fun SuggestionCard( Image( painter = painterResource(icon), contentDescription = null, + contentScale = ContentScale.FillHeight, modifier = Modifier.weight(1f) ) - IconButton( - onClick = onClose, - modifier = Modifier.size(16.dp) - ) { - Icon( - painter = painterResource(R.drawable.ic_x), - contentDescription = null, - tint = Colors.White, - ) + onClose?.let { + IconButton( + onClick = it, + modifier = Modifier.size(16.dp) + ) { + Icon( + painter = painterResource(R.drawable.ic_x), + contentDescription = null, + tint = Colors.White, + ) + } } } @@ -80,7 +86,7 @@ fun SuggestionCard( CaptionB( text = description, - color = Colors.White, + color = captionColor, ) } } diff --git a/app/src/main/java/to/bitkit/ui/screens/shop/ShopDiscoverScreen.kt b/app/src/main/java/to/bitkit/ui/screens/shop/ShopDiscoverScreen.kt index 20af55cb8..7830cedf6 100644 --- a/app/src/main/java/to/bitkit/ui/screens/shop/ShopDiscoverScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/shop/ShopDiscoverScreen.kt @@ -1,98 +1,171 @@ package to.bitkit.ui.screens.shop -import android.annotation.SuppressLint -import android.graphics.Bitmap -import android.view.ViewGroup -import android.webkit.WebResourceError -import android.webkit.WebResourceRequest -import android.webkit.WebView -import android.webkit.WebViewClient -import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.viewinterop.AndroidView +import androidx.compose.ui.unit.dp import to.bitkit.R -import to.bitkit.env.Env +import to.bitkit.models.BitrefillCategory +import to.bitkit.ui.components.BodyM +import to.bitkit.ui.components.SuggestionCard +import to.bitkit.ui.components.Text13Up +import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.CloseNavIcon import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.shared.util.gradientBackground import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.theme.Colors -@SuppressLint("SetJavaScriptEnabled") @Composable fun ShopDiscoverScreen( onClose: () -> Unit, -) { //TODO IMPLEMENT FULL LAYOUT - var isLoading by remember { mutableStateOf(true) } - var webView: WebView? by remember { mutableStateOf(null) } - ScreenColumn { - + onBack: () -> Unit, + navigateWebView: (String, String) -> Unit, //Page, Title +) { + ScreenColumn( + modifier = Modifier.gradientBackground(), + ) { AppTopBar( titleText = stringResource(R.string.other__shop__discover__nav_title), - onBackClick = null, + onBackClick = onBack, actions = { CloseNavIcon(onClick = onClose) }, ) - Box(modifier = Modifier.weight(1f)) { - AndroidView( - modifier = Modifier.fillMaxSize(), - factory = { context -> - WebView(context).apply { - layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ) - webViewClient = object : WebViewClient() { - override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { - super.onPageStarted(view, url, favicon) - isLoading = true - } + LazyColumn( + modifier = Modifier.padding(horizontal = 16.dp), + ) { + item { + VerticalSpacer(16.dp) - override fun onPageFinished(view: WebView?, url: String?) { - super.onPageFinished(view, url) - isLoading = false - } + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + val title = stringResource(R.string.other__shop__discover__gift_cards__title) + SuggestionCard( + modifier = Modifier.weight(1f), + gradientColor = Colors.Green, + title = title, + description = stringResource(R.string.other__shop__discover__gift_cards__description), + icon = R.drawable.gift, + captionColor = Colors.Gray1, + size = 164, + onClick = { + navigateWebView("gift-cards", title) + }, + ) + val title2 = stringResource(R.string.other__shop__discover__esims__title) + SuggestionCard( + modifier = Modifier.weight(1f), + gradientColor = Colors.Yellow, + title = title2, + description = stringResource(R.string.other__shop__discover__esims__description), + icon = R.drawable.globe, + captionColor = Colors.Gray1, + size = 164, + onClick = { + navigateWebView("esims", title2) + }, + ) + } - override fun onReceivedError( - view: WebView?, - request: WebResourceRequest?, - error: WebResourceError? - ) { - super.onReceivedError(view, request, error) - isLoading = false - onClose() - } - } - settings.javaScriptEnabled = true - loadUrl(Env.BIT_REFILL_URL) - webView = this - } - }, - ) + VerticalSpacer(16.dp) + + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + val title = stringResource(R.string.other__shop__discover__refill__title) + SuggestionCard( + modifier = Modifier.weight(1f), + gradientColor = Colors.Purple, + title = title, + description = stringResource(R.string.other__shop__discover__refill__description), + icon = R.drawable.phone, + captionColor = Colors.Gray1, + size = 164, + onClick = { + navigateWebView("refill", title) + }, + ) + val title2 = stringResource(R.string.other__shop__discover__travel__title) + SuggestionCard( + modifier = Modifier.weight(1f), + gradientColor = Colors.Red, + title = title2, + description = stringResource(R.string.other__shop__discover__travel__description), + icon = R.drawable.rocket_2, + size = 164, + captionColor = Colors.Gray1, + onClick = { + navigateWebView("buy/travel", title2) + }, + ) + } + + VerticalSpacer(32.dp) - if (isLoading) { - CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + Text13Up(stringResource(R.string.other__shop__discover__label), color = Colors.White64) + + VerticalSpacer(16.dp) } - } - BackHandler { - webView?.let { - if (it.canGoBack()) { - it.goBack() - } else { - onClose() + items(items = BitrefillCategory.entries.toList(), key = { it.name }) { item -> + Column { + Row( + modifier = Modifier + .padding(top = 8.5.dp, bottom = 10.5.dp) + .clickable { + navigateWebView(item.route, item.title) + }, + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .clip(CircleShape) + .size(32.dp) + .background(Colors.White10), + contentAlignment = Alignment.Center, + ) { + Icon( + imageVector = item.icon, + contentDescription = null, + tint = Colors.White64, + modifier = Modifier.size(16.dp), + ) + } + BodyM( + text = item.title, + modifier = Modifier + .weight(1f) + .padding(horizontal = 8.dp), + ) + Icon( + painter = painterResource(R.drawable.ic_chevron_right), + contentDescription = null, + tint = Colors.White64, + modifier = Modifier.size(24.dp), + ) + } + HorizontalDivider() } - } ?: onClose() + } } } } @@ -101,6 +174,6 @@ fun ShopDiscoverScreen( @Composable private fun Preview() { AppThemeSurface { - ShopDiscoverScreen(onClose = {}) + ShopDiscoverScreen(onClose = {}, onBack = {}, navigateWebView = { _, _ -> }) } } diff --git a/app/src/main/java/to/bitkit/ui/screens/shop/ShopWebViewScreen.kt b/app/src/main/java/to/bitkit/ui/screens/shop/ShopWebViewScreen.kt new file mode 100644 index 000000000..f6381b599 --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/screens/shop/ShopWebViewScreen.kt @@ -0,0 +1,198 @@ +package to.bitkit.ui.screens.shop + +import android.annotation.SuppressLint +import android.graphics.Bitmap +import android.util.Log +import android.view.ViewGroup +import android.webkit.JavascriptInterface +import android.webkit.WebResourceError +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.viewinterop.AndroidView +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import to.bitkit.R +import to.bitkit.env.Env +import to.bitkit.ui.scaffold.AppTopBar +import to.bitkit.ui.scaffold.CloseNavIcon +import to.bitkit.ui.scaffold.ScreenColumn +import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.utils.Logger + +@Serializable +data class WebViewMessage( + val event: String, + @SerialName("paymentUri") + val paymentUri: String? = null +) + +@SuppressLint("SetJavaScriptEnabled") +@Composable +fun ShopWebViewScreen( + onClose: () -> Unit, + onBack: () -> Unit, + onPaymentIntent: (String) -> Unit, + page: String, + title: String, +) { + var isLoading by remember { mutableStateOf(true) } + var webView: WebView? by remember { mutableStateOf(null) } + + // Create a JavaScript interface for message handling + class WebViewInterface { + @JavascriptInterface + fun postMessage(message: String) { + try { + val json = Json { ignoreUnknownKeys = true } + val data = json.decodeFromString(message) + + Log.d("WebView", "Received message: $message") + + when (data.event) { + "payment_intent" -> { + data.paymentUri?.let { uri -> + onPaymentIntent(uri) + } + } + // Add more event types as needed + else -> { + Log.d("WebView", "Unknown event type: ${data.event}") + } + } + } catch (e: Exception) { + Log.e("WebView", "Error parsing message: $message", e) + } + } + } + + ScreenColumn { + AppTopBar( + titleText = "${stringResource(R.string.other__shop__discover__nav_title)} $title", + onBackClick = onBack, + actions = { CloseNavIcon(onClick = onClose) }, + ) + + Box(modifier = Modifier.weight(1f)) { + AndroidView( + modifier = Modifier.fillMaxSize(), + factory = { context -> + WebView(context).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + + webViewClient = object : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + isLoading = true + } + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + isLoading = false + + // Inject JavaScript to bridge postMessage to Android + view?.evaluateJavascript(""" + window.ReactNativeWebView = { + postMessage: function(data) { + Android.postMessage(data); + } + }; + + // Override the default postMessage if it exists + if (window.postMessage) { + window.originalPostMessage = window.postMessage; + window.postMessage = function(data) { + if (typeof data === 'string') { + Android.postMessage(data); + } else { + Android.postMessage(JSON.stringify(data)); + } + }; + } + """.trimIndent(), null) + } + + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError?, + ) { + super.onReceivedError(view, request, error) + Logger.warn("Error: ${error?.description}, Code: ${error?.errorCode}, URL: ${request?.url}", context = "ShopWebViewScreen") + isLoading = false + + error?.let { + if (it.errorCode == WebViewClient.ERROR_HOST_LOOKUP || + it.errorCode == WebViewClient.ERROR_CONNECT || + it.errorCode == WebViewClient.ERROR_TIMEOUT || + it.errorCode == WebViewClient.ERROR_FILE_NOT_FOUND) { + onClose() + } + } + } + } + + // Configure WebView settings + settings.apply { + javaScriptEnabled = true + domStorageEnabled = true + allowContentAccess = true + allowFileAccess = false + } + + addJavascriptInterface(WebViewInterface(), "Android") + + loadUrl(Env.buildBitrefillUri(page = page)) + webView = this + } + }, + ) + + if (isLoading) { + CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + } + } + + BackHandler { + webView?.let { + if (it.canGoBack()) { + it.goBack() + } else { + onBack() + } + } ?: onBack() + } + } +} + +@Preview +@Composable +private fun Preview() { + AppThemeSurface { + ShopWebViewScreen( + onClose = {}, + onBack = {}, + onPaymentIntent = { uri -> + }, + page = "esims", + title = "Gift Cards" + ) + } +} diff --git a/app/src/main/res/drawable-hdpi/rocket_2.webp b/app/src/main/res/drawable-hdpi/rocket_2.webp new file mode 100644 index 000000000..a38137d4c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/rocket_2.webp differ diff --git a/app/src/main/res/drawable-mdpi/rocket_2.webp b/app/src/main/res/drawable-mdpi/rocket_2.webp new file mode 100644 index 000000000..26cbe8800 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/rocket_2.webp differ diff --git a/app/src/main/res/drawable-xhdpi/rocket_2.webp b/app/src/main/res/drawable-xhdpi/rocket_2.webp new file mode 100644 index 000000000..122fbbf8b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/rocket_2.webp differ diff --git a/app/src/main/res/drawable-xxhdpi/rocket_2.webp b/app/src/main/res/drawable-xxhdpi/rocket_2.webp new file mode 100644 index 000000000..5d0f84018 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/rocket_2.webp differ diff --git a/app/src/main/res/drawable-xxxhdpi/rocket_2.webp b/app/src/main/res/drawable-xxxhdpi/rocket_2.webp new file mode 100644 index 000000000..72553bc0d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/rocket_2.webp differ