From f6341971e262cbcce9873c9b96255fe910fcf586 Mon Sep 17 00:00:00 2001 From: Hadia Date: Sat, 17 Dec 2022 04:03:35 +0200 Subject: [PATCH 01/44] feat(dashdirect): add login function with temporary UI (#1068) * feat : move ResponseResource to common module * feat : Add dashdirect network layer * feat :Integrate api to ui * feat : move file to common * fix : code comment --- .../wallet/common/data}/ResponseResource.kt | 2 +- features/exploredash/build.gradle | 1 + .../model/purchase/PurchaseGiftCardRequest.kt | 28 +++++++ .../purchase/PurchaseGiftCardResponse.kt | 52 +++++++++++++ .../data/model/signin/SignInRequest.kt | 26 +++++++ .../data/model/signin/SignInResponse.kt | 41 ++++++++++ .../exploredash/di/ExploreDashModule.kt | 27 ++++++- .../exploredash/network/RemoteDataSource.kt | 60 ++++++++++++++ .../network/interceptor/HeadersInterceptor.kt | 48 ++++++++++++ .../network/service/DashDirectAuthApi.kt | 29 +++++++ .../service/DashDirectClientConstants.kt | 22 ++++++ .../network/service/DashDirectServicesApi.kt | 32 ++++++++ .../repository/DashDirectRepository.kt | 78 +++++++++++++++++++ .../exploredash/ui/ExploreViewModel.kt | 21 ++++- .../features/exploredash/ui/ItemDetails.kt | 2 +- .../features/exploredash/ui/SearchFragment.kt | 54 ++++++++++++- .../exploredash/utils/DashDirectConfig.kt | 64 +++++++++++++++ .../exploredash/utils/DashDirectConstants.kt | 24 ++++++ .../src/main/res/layout/dialog_login.xml | 24 ++++++ .../repository/CoinBaseRepository.kt | 4 +- .../repository/remote/TokenAuthenticator.kt | 4 +- .../viewmodels/CoinbaseActivityViewModel.kt | 2 +- .../CoinbaseBuyDashOrderReviewViewModel.kt | 2 +- .../viewmodels/CoinbaseBuyDashViewModel.kt | 2 +- .../CoinbaseConversionPreviewViewModel.kt | 3 +- .../CoinbaseConvertCryptoViewModel.kt | 2 +- .../viewmodels/CoinbaseServicesViewModel.kt | 2 +- .../viewmodels/EnterTwoFaCodeViewModel.kt | 2 +- .../viewmodels/TransferDashViewModel.kt | 2 +- .../CoinBaseRepositoryTest.kt | 2 +- wallet/build.gradle | 15 ++++ .../schildbach/wallet/WalletApplication.java | 6 ++ .../wallet/ui/buy_sell/BuyAndSellViewModel.kt | 2 +- .../buy_sell/IntegrationOverviewViewModel.kt | 3 +- 34 files changed, 665 insertions(+), 23 deletions(-) rename {integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/network => common/src/main/java/org/dash/wallet/common/data}/ResponseResource.kt (96%) create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardResponse.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInRequest.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInResponse.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/RemoteDataSource.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectAuthApi.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectClientConstants.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConfig.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt create mode 100644 features/exploredash/src/main/res/layout/dialog_login.xml diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/network/ResponseResource.kt b/common/src/main/java/org/dash/wallet/common/data/ResponseResource.kt similarity index 96% rename from integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/network/ResponseResource.kt rename to common/src/main/java/org/dash/wallet/common/data/ResponseResource.kt index fbc38cc7ec..3d8df5ee0c 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/network/ResponseResource.kt +++ b/common/src/main/java/org/dash/wallet/common/data/ResponseResource.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.dash.wallet.integration.coinbase_integration.network +package org.dash.wallet.common.data import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext diff --git a/features/exploredash/build.gradle b/features/exploredash/build.gradle index cec7443406..3f1fd044e9 100644 --- a/features/exploredash/build.gradle +++ b/features/exploredash/build.gradle @@ -63,6 +63,7 @@ dependencies { implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" + implementation "androidx.datastore:datastore-preferences:$datastoreVersion" // Database implementation "androidx.room:room-ktx:$roomVersion" diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt new file mode 100644 index 0000000000..819d0f768f --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.data.model.purchase + +import com.google.gson.annotations.SerializedName + +data class PurchaseGiftCardRequest( + @SerializedName("Currency") + val currency: String? = null, + @SerializedName("GiftCardAmount") + val giftCardAmount: Double? = null, + @SerializedName("MerchantId") + val merchantId: Int? = null +) diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardResponse.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardResponse.kt new file mode 100644 index 0000000000..e4d9c72791 --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardResponse.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.data.model.purchase + +import com.google.gson.annotations.SerializedName + +data class PurchaseGiftCardResponse( + @SerializedName("AccessToken") + val accessToken: Any? = null, + @SerializedName("Data") + val `data`: Data? = null, + @SerializedName("DelayedToken") + val delayedToken: Any? = null, + @SerializedName("ErrorMessage") + val errorMessage: String? = null, + @SerializedName("IsDelayed") + val isDelayed: Boolean? = null, + @SerializedName("Successful") + val successful: Boolean? = null +) { + data class Data( + val amount: Double? = null, + val category: Any? = null, + val currency: String? = null, + val dashAmount: String? = null, + val fiatAmount: String? = null, + val giftCardId: Any? = null, + val name: Any? = null, + val notes: Any? = null, + @SerializedName("order_id") + val orderId: String? = null, + val paymentId: String? = null, + val status: String? = null, + val success: Boolean? = null, + val uri: String? = null, + val userId: Int? = null + ) +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInRequest.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInRequest.kt new file mode 100644 index 0000000000..9a0e66dd0a --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInRequest.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.data.model.signin + +import com.google.gson.annotations.SerializedName + +data class SignInRequest( + @SerializedName("EmailAddress") + val emailAddress: String? = null, + @SerializedName("Password") + val password: String? = null +) diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInResponse.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInResponse.kt new file mode 100644 index 0000000000..f7ec82f14e --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/signin/SignInResponse.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.data.model.signin + +import com.google.gson.annotations.SerializedName + +data class SignInResponse( + @SerializedName("AccessToken") + val accessToken: Any? = null, + @SerializedName("Data") + val `data`: Data? = null, + @SerializedName("DelayedToken") + val delayedToken: Any? = null, + @SerializedName("ErrorMessage") + val errorMessage: String? = null, + @SerializedName("IsDelayed") + val isDelayed: Boolean? = null, + @SerializedName("Successful") + val successful: Boolean? = null +) { + data class Data( + @SerializedName("LegacyMessage") + val legacyMessage: Any? = null, + @SerializedName("Token") + val token: String? = null + ) +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt index d75e1a0489..df54ed0803 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt @@ -33,12 +33,16 @@ import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.ExperimentalCoroutinesApi import org.dash.wallet.features.exploredash.data.ExploreDataSource import org.dash.wallet.features.exploredash.data.MerchantAtmDataSource +import org.dash.wallet.features.exploredash.network.RemoteDataSource +import org.dash.wallet.features.exploredash.network.service.DashDirectAuthApi +import org.dash.wallet.features.exploredash.network.service.DashDirectServicesApi import org.dash.wallet.features.exploredash.repository.DataSyncStatusService import org.dash.wallet.features.exploredash.repository.ExploreDataSyncStatus -import org.dash.wallet.features.exploredash.repository.GCExploreDatabase import org.dash.wallet.features.exploredash.repository.ExploreRepository +import org.dash.wallet.features.exploredash.repository.GCExploreDatabase import org.dash.wallet.features.exploredash.services.UserLocationState import org.dash.wallet.features.exploredash.services.UserLocationStateInt +import org.dash.wallet.features.exploredash.utils.DashDirectConfig @Module @InstallIn(SingletonComponent::class) @@ -64,6 +68,25 @@ abstract class ExploreDashModule { @Provides fun provideFirebaseStorage() = Firebase.storage + + @Provides + fun provideRemoteDataSource(config: DashDirectConfig): RemoteDataSource { + return RemoteDataSource(config) + } + + @Provides + fun provideAuthApi( + remoteDataSource: RemoteDataSource + ): DashDirectAuthApi { + return remoteDataSource.buildApi(DashDirectAuthApi::class.java) + } + + @Provides + fun provideDashDirectApi( + remoteDataSource: RemoteDataSource + ): DashDirectServicesApi { + return remoteDataSource.buildApi(DashDirectServicesApi::class.java) + } } @Binds @@ -86,4 +109,4 @@ abstract class ExploreDashModule { abstract fun bindDataSyncService( exploreDatabase: ExploreDataSyncStatus ): DataSyncStatusService -} \ No newline at end of file +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/RemoteDataSource.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/RemoteDataSource.kt new file mode 100644 index 0000000000..8d32e9fd8e --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/RemoteDataSource.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.network + +import okhttp3.Authenticator +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.dash.wallet.common.BuildConfig +import org.dash.wallet.features.exploredash.network.interceptor.HeadersInterceptor +import org.dash.wallet.features.exploredash.utils.DashDirectConfig +import org.dash.wallet.features.exploredash.utils.DashDirectConstants +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds +import kotlin.time.toJavaDuration + +class RemoteDataSource @Inject constructor( + private val config: DashDirectConfig +) { + + fun buildApi(api: Class): Api { + return Retrofit.Builder() + .baseUrl(DashDirectConstants.BASE_URL) + .client(getOkHttpClient()) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(api) + } + + private fun getOkHttpClient(authenticator: Authenticator? = null): OkHttpClient { + return OkHttpClient.Builder() + .addInterceptor(HeadersInterceptor(config)) + .connectTimeout(20.seconds.toJavaDuration()) + .callTimeout(20.seconds.toJavaDuration()) + .readTimeout(20.seconds.toJavaDuration()) + .also { client -> + authenticator?.let { client.authenticator(it) } + if (BuildConfig.DEBUG) { + val logging = HttpLoggingInterceptor() + logging.level = HttpLoggingInterceptor.Level.BODY + client.addInterceptor(logging) + } + }.build() + } +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt new file mode 100644 index 0000000000..b481ab4135 --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.network.interceptor + +import kotlinx.coroutines.runBlocking +import okhttp3.Interceptor +import okhttp3.Response +import org.dash.wallet.common.Configuration +import org.dash.wallet.features.exploredash.network.service.DashDirectClientConstants +import org.dash.wallet.features.exploredash.utils.DashDirectConfig +import org.dash.wallet.features.exploredash.utils.DashDirectConstants +import javax.inject.Inject + +class HeadersInterceptor @Inject constructor( + private val config: DashDirectConfig +) : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response { + val original = chain.request() + val requestBuilder = original.newBuilder() + requestBuilder.header("Accept", "application/json") + requestBuilder.header(DashDirectConstants.CLIENT_ID, DashDirectClientConstants.CLIENT_ID) + + val accessToken = runBlocking { config.getPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN) } + if (accessToken?.isNotEmpty() == true) { + requestBuilder.header(DashDirectConstants.API_KEY, accessToken) + } + + requestBuilder.method(original.method, original.body) + val request = requestBuilder.build() + + return chain.proceed(request) + } +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectAuthApi.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectAuthApi.kt new file mode 100644 index 0000000000..29b0624492 --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectAuthApi.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.network.service + +import org.dash.wallet.features.exploredash.data.model.signin.SignInRequest +import org.dash.wallet.features.exploredash.data.model.signin.SignInResponse +import retrofit2.http.Body +import retrofit2.http.PUT + +interface DashDirectAuthApi { + @PUT("SignIn") + suspend fun signIn( + @Body signInRequest: SignInRequest + ): SignInResponse? +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectClientConstants.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectClientConstants.kt new file mode 100644 index 0000000000..c3b8d6516b --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectClientConstants.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.network.service + +object DashDirectClientConstants { + @kotlin.jvm.JvmField + var CLIENT_ID = "" +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt new file mode 100644 index 0000000000..922edd8f0d --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.network.service + +import org.dash.wallet.features.exploredash.data.model.purchase.PurchaseGiftCardRequest +import org.dash.wallet.features.exploredash.data.model.purchase.PurchaseGiftCardResponse +import org.dash.wallet.features.exploredash.utils.DashDirectConstants +import retrofit2.http.Body +import retrofit2.http.Header +import retrofit2.http.POST + +interface DashDirectServicesApi { + @POST("PurchaseGiftCard") + suspend fun purchaseGiftCard( + @Header(DashDirectConstants.requestedUUID) deviceID: String, + @Body purchaseGiftCardRequest: PurchaseGiftCardRequest + ): PurchaseGiftCardResponse? +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt new file mode 100644 index 0000000000..4fa1048c2d --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.features.exploredash.repository + +import kotlinx.coroutines.runBlocking +import org.dash.wallet.common.data.ResponseResource +import org.dash.wallet.common.data.safeApiCall +import org.dash.wallet.features.exploredash.data.model.purchase.PurchaseGiftCardRequest +import org.dash.wallet.features.exploredash.data.model.purchase.PurchaseGiftCardResponse +import org.dash.wallet.features.exploredash.data.model.signin.SignInRequest +import org.dash.wallet.features.exploredash.network.service.DashDirectAuthApi +import org.dash.wallet.features.exploredash.network.service.DashDirectServicesApi +import org.dash.wallet.features.exploredash.utils.DashDirectConfig +import javax.inject.Inject + +class DashDirectRepository @Inject constructor( + private val servicesApi: DashDirectServicesApi, + private val authApi: DashDirectAuthApi, + private val config: DashDirectConfig +) : DashDirectRepositoryInt { + + override suspend fun signIn( + email: String, + password: String + ): ResponseResource = safeApiCall { + authApi.signIn(signInRequest = SignInRequest(emailAddress = email, password = password)) + .also { + it?.data?.token?.let { token -> + config.setPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN, token) + } + } + config.getPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN)?.isNotEmpty() ?: false + } + + override fun isUserSignIn() = + runBlocking { config.getPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN)?.isNotEmpty() ?: false } + + fun reset() { + runBlocking { config.setPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN, "") } + } + + override suspend fun purchaseGiftCard( + deviceID: String, + currency: String, + giftCardAmount: Double, + merchantId: Int + ) = safeApiCall { + servicesApi.purchaseGiftCard( + deviceID = deviceID, + purchaseGiftCardRequest = PurchaseGiftCardRequest( + currency = currency, + giftCardAmount = giftCardAmount, + merchantId = merchantId + ) + ) + } +} +interface DashDirectRepositoryInt { + suspend fun signIn(email: String, password: String): ResponseResource + fun isUserSignIn(): Boolean + suspend fun purchaseGiftCard(deviceID: String, currency: String, giftCardAmount: Double, merchantId: Int): + ResponseResource +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt index a38cc80268..4cce8a0327 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt @@ -29,6 +29,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.dash.wallet.common.data.Resource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.common.data.SingleLiveEvent import org.dash.wallet.common.data.Status import org.dash.wallet.common.livedata.ConnectionLiveData @@ -37,11 +38,13 @@ import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.features.exploredash.data.ExploreDataSource import org.dash.wallet.features.exploredash.data.model.* import org.dash.wallet.features.exploredash.data.model.GeoBounds +import org.dash.wallet.features.exploredash.repository.DashDirectRepository import org.dash.wallet.features.exploredash.repository.DataSyncStatusService import org.dash.wallet.features.exploredash.services.UserLocation import org.dash.wallet.features.exploredash.services.UserLocationStateInt import org.dash.wallet.features.exploredash.ui.extensions.Const import org.dash.wallet.features.exploredash.ui.extensions.isMetric +import org.slf4j.LoggerFactory import java.util.* import javax.inject.Inject import kotlin.math.max @@ -77,7 +80,8 @@ class ExploreViewModel @Inject constructor( private val exploreData: ExploreDataSource, private val locationProvider: UserLocationStateInt, private val syncStatusService: DataSyncStatusService, - private val analyticsService: AnalyticsService + private val analyticsService: AnalyticsService, + private val repository: DashDirectRepository ) : ViewModel() { companion object { const val QUERY_DEBOUNCE_VALUE = 300L @@ -87,6 +91,7 @@ class ExploreViewModel @Inject constructor( const val DEFAULT_RADIUS_OPTION = 20 const val MAX_MARKERS = 100 const val DEFAULT_SORT_BY_DISTANCE = true + private val log = LoggerFactory.getLogger(ExploreViewModel::class.java) } private val workerJob = SupervisorJob() @@ -201,6 +206,7 @@ class ExploreViewModel @Inject constructor( val screenState: LiveData get() = _screenState + // Used for the list of search results private val pagingSearchFlow: Flow> = _searchQuery .debounce(QUERY_DEBOUNCE_VALUE) @@ -270,6 +276,7 @@ class ExploreViewModel @Inject constructor( fun init(exploreTopic: ExploreTopic) { + repository.reset() if (this.exploreTopic != exploreTopic) { clearSearchResults() } @@ -390,6 +397,18 @@ class ExploreViewModel @Inject constructor( this.allMerchantLocationsJob?.cancel() } + fun isUserSignInDashDirect() = repository.isUserSignIn() + + suspend fun signInToDashDirect(email: String, password: String) = repository.signIn(email, password) + + suspend fun purchaseGiftCard(deviceID: String, currency: String, giftCardAmount: Double, merchantId: Int) = + repository.purchaseGiftCard( + deviceID = deviceID, + giftCardAmount = giftCardAmount, + currency = currency, + merchantId = merchantId + ) + fun onMapMarkerSelected(id: Int) { val item = _allMerchantLocations.value?.firstOrNull { it.id == id } ?: _physicalSearchResults.value?.firstOrNull { it.id == id } diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ItemDetails.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ItemDetails.kt index 37ad24fc7b..61549550ac 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ItemDetails.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ItemDetails.kt @@ -170,7 +170,7 @@ class ItemDetails(context: Context, attrs: AttributeSet): LinearLayout(context, payBtn.isVisible = !merchant.deeplink.isNullOrBlank() payBtn.text = context.getText(R.string.explore_buy_gift_card) payBtn.setOnClickListener { - openDeeplink(merchant.deeplink!!) + //openDeeplink(merchant.deeplink!!) onBuyGiftCardButtonClicked?.invoke() } } diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt index 0a7fc4725d..d2bf1435fe 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt @@ -19,13 +19,17 @@ package org.dash.wallet.features.exploredash.ui import android.animation.AnimatorSet import android.animation.ObjectAnimator +import android.app.AlertDialog import android.content.Context import android.os.Bundle import android.os.Handler import android.os.Looper +import android.util.Log import android.view.View import android.view.inputmethod.InputMethodManager +import android.widget.Toast import androidx.activity.OnBackPressedCallback +import androidx.appcompat.widget.AppCompatEditText import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.animation.doOnEnd import androidx.core.content.ContextCompat @@ -35,6 +39,7 @@ import androidx.core.view.* import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.ConcatAdapter @@ -58,6 +63,7 @@ import org.dash.wallet.features.exploredash.ui.adapters.SearchHeaderAdapter import org.dash.wallet.features.exploredash.ui.extensions.* import org.dash.wallet.common.Configuration import org.dash.wallet.common.data.Resource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.common.data.Status import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService @@ -381,6 +387,45 @@ class SearchFragment : Fragment(R.layout.fragment_search) { } } + fun showLoginDialog() { + val builder = AlertDialog.Builder(requireContext()) + val inflater = layoutInflater + builder.setTitle("Login to dash direct ") + val dialogLayout = inflater.inflate(R.layout.dialog_login, null) + val email = dialogLayout.findViewById(R.id.email) + val password = dialogLayout.findViewById(R.id.password) + builder.setView(dialogLayout) + builder.setPositiveButton("login") { _, _ -> + signInToDashDirect(email, password) + } + builder.show() + } + + private fun signInToDashDirect( + email: AppCompatEditText, + password: AppCompatEditText + ) { + lifecycleScope.launch { + when (val response = viewModel.signInToDashDirect(email.text.toString(), password.text.toString())) { + is ResponseResource.Success -> { + if (response.value) { + // TODO open Buy card UI + Toast.makeText(requireContext(), "Open Buy Card", Toast.LENGTH_SHORT).show() + viewModel.logEvent(AnalyticsConstants.Explore.MERCHANT_DETAILS_BUY_GIFT_CARD) + } + } + + is ResponseResource.Failure -> { + Toast.makeText( + requireContext(), + "Dash direct error ${response.errorCode}: ${response.errorBody ?: "empty"}", + Toast.LENGTH_SHORT + ).show() + } + } + } + } + private fun setupSearchInput(bottomSheet: BottomSheetBehavior) { searchHeaderAdapter.setOnSearchQueryChanged { binding.noResultsPanel.isVisible = false @@ -500,7 +545,14 @@ class SearchFragment : Fragment(R.layout.fragment_search) { viewModel.logEvent(AnalyticsConstants.Explore.MERCHANT_DETAILS_OPEN_WEBSITE) } } - binding.itemDetails.setOnBuyGiftCardButtonClicked { viewModel.logEvent(AnalyticsConstants.Explore.MERCHANT_DETAILS_BUY_GIFT_CARD) } + + binding.itemDetails.setOnBuyGiftCardButtonClicked { + if (!viewModel.isUserSignInDashDirect()) { + showLoginDialog() + } else { + Toast.makeText(requireContext(), "Open Buy Card", Toast.LENGTH_SHORT).show() + } + } } private fun setupScreenTransitions() { diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConfig.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConfig.kt new file mode 100644 index 0000000000..fec0c65fda --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConfig.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2022 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.features.exploredash.utils + +import android.content.Context +import androidx.datastore.preferences.core.* +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import java.io.IOException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DashDirectConfig @Inject constructor(private val context: Context) { + companion object { + val PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN = stringPreferencesKey("last_dash_direct_access_token") + } + + private val Context.dataStore by preferencesDataStore("dashdirect") + private val dataStore = context.dataStore.data + .catch { exception -> + if (exception is IOException) { + emit(emptyPreferences()) + } else { + throw exception + } + } + + fun observePreference(key: Preferences.Key): Flow { + return dataStore.map { preferences -> preferences[key] } + } + + suspend fun getPreference(key: Preferences.Key): T? { + return dataStore.map { preferences -> preferences[key] }.first() + } + + suspend fun setPreference(key: Preferences.Key, value: T) { + context.dataStore.edit { preferences -> + preferences[key] = value + } + } + + suspend fun clearAll() { + context.dataStore.edit { it.clear() } + } +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt new file mode 100644 index 0000000000..3c05149020 --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.utils + +object DashDirectConstants { + const val BASE_URL = "https://apidev.dashdirect.org/DashDirect/" + const val API_KEY = "apikey" + const val requestedUUID = "requestedUUID" + const val CLIENT_ID = "clientId" +} diff --git a/features/exploredash/src/main/res/layout/dialog_login.xml b/features/exploredash/src/main/res/layout/dialog_login.xml new file mode 100644 index 0000000000..bb89e1b27a --- /dev/null +++ b/features/exploredash/src/main/res/layout/dialog_login.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/CoinBaseRepository.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/CoinBaseRepository.kt index 57bd7cabf1..8b5420ff0b 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/CoinBaseRepository.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/CoinBaseRepository.kt @@ -21,8 +21,8 @@ import org.dash.wallet.common.Configuration import org.dash.wallet.common.util.Constants import org.dash.wallet.integration.coinbase_integration.* import org.dash.wallet.integration.coinbase_integration.model.* -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource -import org.dash.wallet.integration.coinbase_integration.network.safeApiCall +import org.dash.wallet.common.data.ResponseResource +import org.dash.wallet.common.data.safeApiCall import org.dash.wallet.integration.coinbase_integration.service.CoinBaseAuthApi import org.dash.wallet.integration.coinbase_integration.service.CoinBaseClientConstants import org.dash.wallet.integration.coinbase_integration.service.CoinBaseServicesApi diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/remote/TokenAuthenticator.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/remote/TokenAuthenticator.kt index 166feb98af..9c28ec51c8 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/remote/TokenAuthenticator.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/repository/remote/TokenAuthenticator.kt @@ -23,8 +23,8 @@ import okhttp3.Response import okhttp3.Route import org.dash.wallet.common.Configuration import org.dash.wallet.integration.coinbase_integration.model.TokenResponse -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource -import org.dash.wallet.integration.coinbase_integration.network.safeApiCall +import org.dash.wallet.common.data.ResponseResource +import org.dash.wallet.common.data.safeApiCall import org.dash.wallet.integration.coinbase_integration.service.CloseCoinbasePortalBroadcaster import org.dash.wallet.integration.coinbase_integration.service.CoinBaseTokenRefreshApi import org.dash.wallet.integration.coinbase_integration.utils.CoinbaseConfig diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseActivityViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseActivityViewModel.kt index 19b6ebd9b0..848f5c638f 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseActivityViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseActivityViewModel.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.runBlocking import org.dash.wallet.common.Configuration import org.dash.wallet.common.ui.payment_method_picker.PaymentMethod import org.dash.wallet.common.ui.payment_method_picker.PaymentMethodType -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import org.dash.wallet.integration.coinbase_integration.ui.convert_currency.model.BaseIdForFaitDataUIState import org.dash.wallet.integration.coinbase_integration.ui.convert_currency.model.PaymentMethodsUiState diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashOrderReviewViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashOrderReviewViewModel.kt index eb12af7d2e..73985ab653 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashOrderReviewViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashOrderReviewViewModel.kt @@ -32,7 +32,7 @@ import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.ui.ConnectivityViewModel import org.dash.wallet.integration.coinbase_integration.model.* -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import java.util.* import javax.inject.Inject diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashViewModel.kt index 829311aa4f..8dd4fa49d8 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseBuyDashViewModel.kt @@ -39,7 +39,7 @@ import org.dash.wallet.common.util.Constants import org.dash.wallet.common.util.GenericUtils import org.dash.wallet.integration.coinbase_integration.CoinbaseConstants import org.dash.wallet.integration.coinbase_integration.model.* -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import java.lang.NumberFormatException import javax.inject.Inject diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConversionPreviewViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConversionPreviewViewModel.kt index 069a9e4637..26b6981bfc 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConversionPreviewViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConversionPreviewViewModel.kt @@ -16,7 +16,6 @@ */ package org.dash.wallet.integration.coinbase_integration.viewmodels -import android.app.Service import androidx.core.os.bundleOf import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -40,7 +39,7 @@ import org.dash.wallet.common.ui.ConnectivityViewModel import org.dash.wallet.common.util.Constants import org.dash.wallet.integration.coinbase_integration.CoinbaseConstants import org.dash.wallet.integration.coinbase_integration.model.* -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import java.util.* import javax.inject.Inject diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConvertCryptoViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConvertCryptoViewModel.kt index 6b9ce90da5..29de985ecb 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConvertCryptoViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseConvertCryptoViewModel.kt @@ -39,7 +39,7 @@ import org.dash.wallet.common.util.Constants import org.dash.wallet.common.util.GenericUtils import org.dash.wallet.integration.coinbase_integration.CoinbaseConstants import org.dash.wallet.integration.coinbase_integration.model.* -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import org.dash.wallet.integration.coinbase_integration.utils.CoinbaseConfig import javax.inject.Inject diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseServicesViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseServicesViewModel.kt index a8579fa0df..b4f6688221 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseServicesViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/CoinbaseServicesViewModel.kt @@ -37,7 +37,7 @@ import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.ui.ConnectivityViewModel import org.dash.wallet.integration.coinbase_integration.model.CoinBaseUserAccountData -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import org.dash.wallet.integration.coinbase_integration.utils.CoinbaseConfig import javax.inject.Inject diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/EnterTwoFaCodeViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/EnterTwoFaCodeViewModel.kt index 04462865ae..b68aa5d02c 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/EnterTwoFaCodeViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/EnterTwoFaCodeViewModel.kt @@ -31,7 +31,7 @@ import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.integration.coinbase_integration.CoinbaseConstants import org.dash.wallet.integration.coinbase_integration.model.CoinbaseErrorResponse import org.dash.wallet.integration.coinbase_integration.model.SendTransactionToWalletParams -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import org.dash.wallet.integration.coinbase_integration.ui.dialogs.CoinBaseResultDialog import java.io.IOException diff --git a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/TransferDashViewModel.kt b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/TransferDashViewModel.kt index bac2457cde..160675b2e2 100644 --- a/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/TransferDashViewModel.kt +++ b/integrations/coinbase/src/main/java/org/dash/wallet/integration/coinbase_integration/viewmodels/TransferDashViewModel.kt @@ -35,7 +35,7 @@ import org.dash.wallet.integration.coinbase_integration.model.CoinbaseToDashExch import org.dash.wallet.integration.coinbase_integration.model.CoinbaseTransactionParams import org.dash.wallet.integration.coinbase_integration.model.SendTransactionToWalletParams import org.dash.wallet.integration.coinbase_integration.model.TransactionType -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import org.dash.wallet.integration.coinbase_integration.ui.convert_currency.model.SwapValueErrorType import org.dash.wallet.integration.coinbase_integration.ui.dialogs.CoinBaseResultDialog diff --git a/integrations/coinbase/src/test/java/org/dash/wallet/integration/coinbase_integration/CoinBaseRepositoryTest.kt b/integrations/coinbase/src/test/java/org/dash/wallet/integration/coinbase_integration/CoinBaseRepositoryTest.kt index 99829ad5fd..a6c7d0e62d 100644 --- a/integrations/coinbase/src/test/java/org/dash/wallet/integration/coinbase_integration/CoinBaseRepositoryTest.kt +++ b/integrations/coinbase/src/test/java/org/dash/wallet/integration/coinbase_integration/CoinBaseRepositoryTest.kt @@ -25,7 +25,7 @@ import org.dash.wallet.common.Configuration import org.dash.wallet.integration.coinbase_integration.model.PlaceBuyOrderParams import org.dash.wallet.integration.coinbase_integration.model.PlaceBuyOrderUIModel import org.dash.wallet.integration.coinbase_integration.model.SendTransactionToWalletParams -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepository import org.dash.wallet.integration.coinbase_integration.service.CoinBaseAuthApi import org.dash.wallet.integration.coinbase_integration.service.CoinBaseServicesApi diff --git a/wallet/build.gradle b/wallet/build.gradle index 6534ea4948..cdf61e1469 100644 --- a/wallet/build.gradle +++ b/wallet/build.gradle @@ -206,6 +206,7 @@ android { buildConfigField("String", "COINBASE_CLIENT_ID", "\"COINBASE_CLIENT_ID\"") buildConfigField("String", "COINBASE_CLIENT_SECRET", "\"COINBASE_CLIENT_SECRET\"") + buildConfigField("String", "DASHDIRECT_CLIENT_ID", "\"DASHDIRECT_CLIENT_ID\"") def supportEmail = localProps.getProperty("SUPPORT_EMAIL") if (supportEmail != null) { @@ -289,6 +290,14 @@ android { buildConfigField("String", "COINBASE_CLIENT_ID", coinbaseClientId) buildConfigField("String", "COINBASE_CLIENT_SECRET", coinbaseClientSecret) + def dashDirectClientId = props.getProperty("DASHDIRECT_CLIENT_ID") + + if (dashDirectClientId == null) { + dashDirectClientId = "\"DASHDIRECT_CLIENT_ID\"" + } + + buildConfigField("String", "DASHDIRECT_CLIENT_ID", dashDirectClientId) + } _testNet3 { applicationId = "hashengineering.darkcoin.wallet_test" @@ -315,6 +324,12 @@ android { buildConfigField("String", "COINBASE_CLIENT_ID", coinbaseClientId) buildConfigField("String", "COINBASE_CLIENT_SECRET", coinbaseClientSecret) + def dashDirectClientId = props.getProperty("DASHDIRECT_CLIENT_ID") + + if (dashDirectClientId == null) { + dashDirectClientId = "\"DASHDIRECT_CLIENT_ID\"" + } + buildConfigField("String", "DASHDIRECT_CLIENT_ID", dashDirectClientId) } } packagingOptions { diff --git a/wallet/src/de/schildbach/wallet/WalletApplication.java b/wallet/src/de/schildbach/wallet/WalletApplication.java index 4a72ee13c9..a7ebfcc0da 100644 --- a/wallet/src/de/schildbach/wallet/WalletApplication.java +++ b/wallet/src/de/schildbach/wallet/WalletApplication.java @@ -83,6 +83,7 @@ import org.dash.wallet.common.transactions.TransactionWrapper; import org.dash.wallet.features.exploredash.ExploreSyncWorker; import org.dash.wallet.common.services.TransactionMetadataProvider; +import org.dash.wallet.features.exploredash.network.service.DashDirectClientConstants; import org.dash.wallet.integration.coinbase_integration.service.CoinBaseClientConstants; import de.schildbach.wallet.ui.buy_sell.LiquidClient; import org.dash.wallet.integration.uphold.api.UpholdClient; @@ -373,6 +374,7 @@ public void finalizeInitialization() { initUphold(); initCoinbase(); + initDashDirect(); } private void initUphold() { @@ -393,6 +395,10 @@ private void initCoinbase() { CoinBaseClientConstants.CLIENT_SECRET = BuildConfig.COINBASE_CLIENT_SECRET; } + private void initDashDirect() { + DashDirectClientConstants.CLIENT_ID = BuildConfig.DASHDIRECT_CLIENT_ID; + } + @TargetApi(Build.VERSION_CODES.O) private void createNotificationChannels() { // Transactions diff --git a/wallet/src/de/schildbach/wallet/ui/buy_sell/BuyAndSellViewModel.kt b/wallet/src/de/schildbach/wallet/ui/buy_sell/BuyAndSellViewModel.kt index 2aef1c3f45..1a7d0270fb 100644 --- a/wallet/src/de/schildbach/wallet/ui/buy_sell/BuyAndSellViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/buy_sell/BuyAndSellViewModel.kt @@ -36,7 +36,7 @@ import org.dash.wallet.common.services.ExchangeRatesProvider import org.dash.wallet.common.services.analytics.AnalyticsConstants import org.dash.wallet.common.services.analytics.AnalyticsService import org.dash.wallet.common.ui.ConnectivityViewModel -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepository import org.dash.wallet.integration.coinbase_integration.utils.CoinbaseConfig import org.dash.wallet.integration.uphold.api.UpholdClient diff --git a/wallet/src/de/schildbach/wallet/ui/buy_sell/IntegrationOverviewViewModel.kt b/wallet/src/de/schildbach/wallet/ui/buy_sell/IntegrationOverviewViewModel.kt index 03be3a5707..2e93d8fe0b 100644 --- a/wallet/src/de/schildbach/wallet/ui/buy_sell/IntegrationOverviewViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/buy_sell/IntegrationOverviewViewModel.kt @@ -22,10 +22,9 @@ import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import org.dash.wallet.common.Configuration import org.dash.wallet.common.services.analytics.AnalyticsService -import org.dash.wallet.integration.coinbase_integration.network.ResponseResource +import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.integration.coinbase_integration.repository.CoinBaseRepositoryInt import org.dash.wallet.integration.uphold.api.UpholdClient -import org.dash.wallet.integration.uphold.api.getAccessToken import org.slf4j.LoggerFactory import javax.inject.Inject From b614bed2401540d127054683593bce4f71565d96 Mon Sep 17 00:00:00 2001 From: Hadia Date: Fri, 13 Jan 2023 19:45:26 +0200 Subject: [PATCH 02/44] feat(dashdirect): add purchase gift card UI (#1079) * fix: ignore address assignment for intent with no address * feat : create payment header * feat : Refactor amount view * feat : create PurchaseGiftCardFragment * feat : create PurchaseGiftCardConfirmDialog * feat : create GiftCardDetailsDialog * feat : Add Strings * feat : create GiftCardDetailsDialog * feat : refactor ExploreViewModel.kt * feat : refactor isUserSignIn.kt * feat : remove mock data * feat : fix test * feat : check if the transaction id is empty * feat : mock login * feat : fix navigation * feat : fix the amount issue * feat : refactor purchaseGiftCardData * feat : fix the scope of ExploreViewModel * feat : fix Purchase gift card * feat : fix show currency picker Co-authored-by: Andrei Ashikhmin --- .../wallet/common/ui/PaymentHeaderView.kt | 99 ++++ .../common/ui/enter_amount/AmountView.kt | 12 +- .../ui/enter_amount/EnterAmountFragment.kt | 24 +- .../ui/enter_amount/EnterAmountViewModel.kt | 12 +- common/src/main/res/drawable/ic_hide.xml | 9 + common/src/main/res/drawable/ic_show.xml | 9 + .../main/res/layout/payment_header_view.xml | 88 ++++ common/src/main/res/values/styles.xml | 6 + features/exploredash/build.gradle | 2 + .../data/model/GiftCardDetailsDialogModel.kt | 16 + .../model/purchase/PurchaseGiftCardRequest.kt | 2 +- .../exploredash/di/ExploreDashModule.kt | 10 +- .../network/interceptor/HeadersInterceptor.kt | 7 +- .../network/service/DashDirectServicesApi.kt | 1 + .../repository/DashDirectRepository.kt | 11 +- .../exploredash/ui/ExploreMapFragment.kt | 3 +- .../exploredash/ui/ExploreViewModel.kt | 17 +- .../ui/PurchaseGiftCardFragment.kt | 166 +++++++ .../ui/PurchaseGiftCardViewModel.kt | 91 ++++ .../features/exploredash/ui/SearchFragment.kt | 16 +- .../exploredash/ui/dialogs/FiltersDialog.kt | 3 +- .../ui/dialogs/GiftCardDetailsDialog.kt | 129 +++++ .../dialogs/PurchaseGiftCardConfirmDialog.kt | 112 +++++ .../exploredash/utils/DashDirectConstants.kt | 2 + .../main/res/drawable/ic_dashdirect_logo.xml | 18 + .../src/main/res/drawable/ic_instore_blue.xml | 12 + .../src/main/res/drawable/ic_online_blue.xml | 9 + .../res/drawable/ic_self_chekout_blue.xml | 9 + .../dialog_confirm_purchase_gift_card.xml | 234 +++++++++ .../res/layout/dialog_gift_card_details.xml | 463 ++++++++++++++++++ .../layout/fragment_purchase_gift_card.xml | 80 +++ .../src/main/res/navigation/explore_dash.xml | 11 + .../main/res/values/strings-explore-dash.xml | 29 ++ .../exploredash/ExploreViewModelTest.kt | 18 +- wallet/res/navigation/nav_home.xml | 14 + .../TransactionDetailsDialogFragment.kt | 18 +- 36 files changed, 1708 insertions(+), 54 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/ui/PaymentHeaderView.kt create mode 100644 common/src/main/res/drawable/ic_hide.xml create mode 100644 common/src/main/res/drawable/ic_show.xml create mode 100644 common/src/main/res/layout/payment_header_view.xml create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/GiftCardDetailsDialogModel.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardFragment.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardViewModel.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/GiftCardDetailsDialog.kt create mode 100644 features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/PurchaseGiftCardConfirmDialog.kt create mode 100644 features/exploredash/src/main/res/drawable/ic_dashdirect_logo.xml create mode 100644 features/exploredash/src/main/res/drawable/ic_instore_blue.xml create mode 100644 features/exploredash/src/main/res/drawable/ic_online_blue.xml create mode 100644 features/exploredash/src/main/res/drawable/ic_self_chekout_blue.xml create mode 100644 features/exploredash/src/main/res/layout/dialog_confirm_purchase_gift_card.xml create mode 100644 features/exploredash/src/main/res/layout/dialog_gift_card_details.xml create mode 100644 features/exploredash/src/main/res/layout/fragment_purchase_gift_card.xml diff --git a/common/src/main/java/org/dash/wallet/common/ui/PaymentHeaderView.kt b/common/src/main/java/org/dash/wallet/common/ui/PaymentHeaderView.kt new file mode 100644 index 0000000000..fd825c7f44 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/ui/PaymentHeaderView.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.common.ui + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import org.dash.wallet.common.R +import org.dash.wallet.common.databinding.PaymentHeaderViewBinding + +class PaymentHeaderView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr) { + + private var binding: PaymentHeaderViewBinding = PaymentHeaderViewBinding.inflate(LayoutInflater.from(context), this, true) + + private var revealBalance = false + + private var onHideBalanceClickedListener: OnHideBalanceClickedListener? = null + + init { + binding.hideButton.setOnClickListener { + revealBalance = !revealBalance + if (revealBalance) { + binding.hideButton.setImageResource(R.drawable.ic_show) + } else { + binding.hideButton.setImageResource(R.drawable.ic_hide) + } + onHideBalanceClickedListener?.onHideBalanceClicked(it) + } + } + + fun setPaymentAddressViewIcon(drawable: Int) { + binding.paymentAddressViewIcon.setImageResource(drawable) + binding.paymentAddressViewIcon.isVisible = true + } + + fun setPaymentAddressViewIcon(imageUrl: String?) { + imageUrl?.let { + Glide.with(context) + .load(it) + .placeholder(R.drawable.ic_image_placeholder) + .error(R.drawable.ic_image_placeholder) + .transition(DrawableTransitionOptions.withCrossFade(200)) + .into(binding.paymentAddressViewIcon) + binding.paymentAddressViewIcon.isVisible = true + } + } + + fun setPaymentAddressViewTitle(title: String) { + binding.paymentAddressViewTitle.text = title + } + + fun setPaymentAddressViewProposition(title: String) { + binding.paymentAddressViewProposition.text = title + } + + fun setPaymentAddressViewSubtitle(title: String) { + binding.paymentAddressViewSubtitle.text = title + } + + fun setBalanceValue(balanceText: String) { + if (revealBalance) { + binding.balanceLabel.text = balanceText + } else { + binding.balanceLabel.text = "**********" + } + } + + fun setOnHideBalanceClickedListener(onHideBalanceClickedListener: OnHideBalanceClickedListener) { + this.onHideBalanceClickedListener = onHideBalanceClickedListener + } +} + +interface OnHideBalanceClickedListener { + fun onHideBalanceClicked(view: View) +} diff --git a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt index e441f008b8..43c373dc0b 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/AmountView.kt @@ -27,6 +27,7 @@ import android.view.LayoutInflater import android.view.View import android.widget.PopupMenu import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.view.updatePadding import org.bitcoinj.core.Coin @@ -41,7 +42,6 @@ import java.text.DecimalFormat import java.text.NumberFormat import java.util.* - class AmountView(context: Context, attrs: AttributeSet) : ConstraintLayout(context, attrs) { private val binding = AmountViewBinding.inflate(LayoutInflater.from(context), this) val dashFormat = MonetaryFormat().withLocale(GenericUtils.getDeviceLocale()) @@ -90,14 +90,14 @@ class AmountView(context: Context, attrs: AttributeSet) : ConstraintLayout(conte if (value) { input = dashFormat.minDecimals(0) - .optionalDecimals(0,6).format(dashAmount).toString() + .optionalDecimals(0, 6).format(dashAmount).toString() } else { binding.resultAmount.text = dashFormat.format(dashAmount) exchangeRate?.let { fiatAmount = it.coinToFiat(dashAmount) _input = fiatFormat.minDecimals(0) - .optionalDecimals(0,2).format(fiatAmount).toString() + .optionalDecimals(0, 2).format(fiatAmount).toString() binding.inputAmount.text = formatInputWithCurrency() } } @@ -111,6 +111,12 @@ class AmountView(context: Context, attrs: AttributeSet) : ConstraintLayout(conte binding.resultCurrencyToggle.isVisible = dashToFiat && value } + var showResultContainer: Boolean = true + set(value) { + field = value + binding.resultContainer.isVisible = !dashToFiat && value + } + init { val padding = resources.getDimensionPixelOffset(R.dimen.default_horizontal_padding) updatePadding(left = padding, right = padding) diff --git a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountFragment.kt b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountFragment.kt index 8d1bc96ec7..217dbbb640 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountFragment.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountFragment.kt @@ -38,24 +38,33 @@ import org.dash.wallet.common.util.GenericUtils import java.text.DecimalFormatSymbols @AndroidEntryPoint -class EnterAmountFragment: Fragment(R.layout.fragment_enter_amount) { +class EnterAmountFragment : Fragment(R.layout.fragment_enter_amount) { companion object { private const val ARG_INITIAL_AMOUNT = "initial_amount" private const val ARG_DASH_TO_FIAT = "dash_to_fiat" private const val ARG_MAX_BUTTON_VISIBLE = "max_visible" private const val ARG_SHOW_CURRENCY_SELECTOR_BUTTON = "show_currency_selector" + private const val ARG_CURRENCY_OPTIONS_PICKER_VISIBLE = "currency_options_picker_visible" + private const val ARG_SHOW_AMOUNT_RESULT_CONTAINER = "show_amount_result_container" + private const val ARG_CURRENCY_Code = "CURRENCY_Code" @JvmStatic fun newInstance( dashToFiat: Boolean = false, initialAmount: Monetary? = null, isMaxButtonVisible: Boolean = true, - showCurrencySelector: Boolean = true + showCurrencySelector: Boolean = true, + isCurrencyOptionsPickerVisible: Boolean = true, + showAmountResultContainer: Boolean = true, + faitCurrencyCode: String? = null ): EnterAmountFragment { val args = bundleOf( ARG_DASH_TO_FIAT to dashToFiat, ARG_MAX_BUTTON_VISIBLE to isMaxButtonVisible, - ARG_SHOW_CURRENCY_SELECTOR_BUTTON to showCurrencySelector + ARG_SHOW_CURRENCY_SELECTOR_BUTTON to showCurrencySelector, + ARG_CURRENCY_OPTIONS_PICKER_VISIBLE to isCurrencyOptionsPickerVisible, + ARG_SHOW_AMOUNT_RESULT_CONTAINER to showAmountResultContainer, + ARG_CURRENCY_Code to faitCurrencyCode ) initialAmount?.let { args.putSerializable(ARG_INITIAL_AMOUNT, it) } @@ -77,6 +86,13 @@ class EnterAmountFragment: Fragment(R.layout.fragment_enter_amount) { val args = requireArguments() binding.maxButtonWrapper.isVisible = args.getBoolean(ARG_MAX_BUTTON_VISIBLE) binding.amountView.showCurrencySelector = args.getBoolean(ARG_SHOW_CURRENCY_SELECTOR_BUTTON) + binding.amountView.showResultContainer = args.getBoolean(ARG_SHOW_AMOUNT_RESULT_CONTAINER) + binding.currencyOptions.isVisible = args.getBoolean(ARG_CURRENCY_OPTIONS_PICKER_VISIBLE) + + args.getString(ARG_CURRENCY_Code)?.let { rateCurrencyCode -> + viewModel.selectedCurrencyCode = rateCurrencyCode + } + val dashToFiat = args.getBoolean(ARG_DASH_TO_FIAT) binding.amountView.dashToFiat = dashToFiat @@ -259,4 +275,4 @@ class EnterAmountFragment: Fragment(R.layout.fragment_enter_amount) { binding.networkStatusStub.isVisible = !hasInternet } } -} \ No newline at end of file +} diff --git a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt index 61f4478ec1..4aec1564a7 100644 --- a/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt +++ b/common/src/main/java/org/dash/wallet/common/ui/enter_amount/EnterAmountViewModel.kt @@ -69,14 +69,21 @@ class EnterAmountViewModel @Inject constructor( get() = _callerBlocksContinue.value ?: false set(value) { _callerBlocksContinue.value = value } + private var _minIsIncluded = false + val canContinue: LiveData get() = MediatorLiveData().apply { fun canContinue(): Boolean { val amount = _amount.value ?: Coin.ZERO val minAmount = _minAmount.value ?: Coin.ZERO val maxAmount = _maxAmount.value ?: Coin.ZERO + val minCheck = if (_minIsIncluded) { + amount >= minAmount + } else { + amount > minAmount + } - return !blockContinue && amount > minAmount && (maxAmount == Coin.ZERO || amount <= maxAmount) + return !blockContinue && minCheck && (maxAmount == Coin.ZERO || amount <= maxAmount) } addSource(_callerBlocksContinue) { value = canContinue() } @@ -100,7 +107,8 @@ class EnterAmountViewModel @Inject constructor( _maxAmount.value = coin } - fun setMinAmount(coin: Coin) { + fun setMinAmount(coin: Coin, isIncludedMin: Boolean = false) { _minAmount.value = coin + _minIsIncluded = isIncludedMin } } diff --git a/common/src/main/res/drawable/ic_hide.xml b/common/src/main/res/drawable/ic_hide.xml new file mode 100644 index 0000000000..ac35230ea8 --- /dev/null +++ b/common/src/main/res/drawable/ic_hide.xml @@ -0,0 +1,9 @@ + + + diff --git a/common/src/main/res/drawable/ic_show.xml b/common/src/main/res/drawable/ic_show.xml new file mode 100644 index 0000000000..9593598633 --- /dev/null +++ b/common/src/main/res/drawable/ic_show.xml @@ -0,0 +1,9 @@ + + + diff --git a/common/src/main/res/layout/payment_header_view.xml b/common/src/main/res/layout/payment_header_view.xml new file mode 100644 index 0000000000..e5512d94db --- /dev/null +++ b/common/src/main/res/layout/payment_header_view.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/src/main/res/values/styles.xml b/common/src/main/res/values/styles.xml index 98193e4cae..a8e1c0e299 100644 --- a/common/src/main/res/values/styles.xml +++ b/common/src/main/res/values/styles.xml @@ -1222,4 +1222,10 @@ 2dp 15dp + + \ No newline at end of file diff --git a/features/exploredash/build.gradle b/features/exploredash/build.gradle index 3f1fd044e9..80207364a8 100644 --- a/features/exploredash/build.gradle +++ b/features/exploredash/build.gradle @@ -4,6 +4,7 @@ plugins { id 'kotlin-kapt' id 'androidx.navigation.safeargs.kotlin' id 'dagger.hilt.android.plugin' + id 'kotlin-parcelize' } android { @@ -58,6 +59,7 @@ dependencies { implementation "androidx.appcompat:appcompat:$appCompatVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutines_version" implementation "androidx.work:work-runtime-ktx:$workRuntimeVersion" + implementation "org.dashj:dashj-core:$dashjVersion" // Architecture implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/GiftCardDetailsDialogModel.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/GiftCardDetailsDialogModel.kt new file mode 100644 index 0000000000..f0b356ff37 --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/GiftCardDetailsDialogModel.kt @@ -0,0 +1,16 @@ +package org.dash.wallet.features.exploredash.data.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class GiftCardDetailsDialogModel( + val merchantName: String? = "", + val merchantLogo: String? = "", + val barcodeImg: String? = "", + val giftCardPrice: String? = "", + val giftCardCheckCurrentBalance: String? = "", + val giftCardNumber: String? = "", + val giftCardPin: String? = "", + val transactionId: String? = "" +) : Parcelable diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt index 819d0f768f..0c6b2a7709 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/data/model/purchase/PurchaseGiftCardRequest.kt @@ -24,5 +24,5 @@ data class PurchaseGiftCardRequest( @SerializedName("GiftCardAmount") val giftCardAmount: Double? = null, @SerializedName("MerchantId") - val merchantId: Int? = null + val merchantId: Long? = null ) diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt index df54ed0803..6f48485514 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/di/ExploreDashModule.kt @@ -36,10 +36,7 @@ import org.dash.wallet.features.exploredash.data.MerchantAtmDataSource import org.dash.wallet.features.exploredash.network.RemoteDataSource import org.dash.wallet.features.exploredash.network.service.DashDirectAuthApi import org.dash.wallet.features.exploredash.network.service.DashDirectServicesApi -import org.dash.wallet.features.exploredash.repository.DataSyncStatusService -import org.dash.wallet.features.exploredash.repository.ExploreDataSyncStatus -import org.dash.wallet.features.exploredash.repository.ExploreRepository -import org.dash.wallet.features.exploredash.repository.GCExploreDatabase +import org.dash.wallet.features.exploredash.repository.* import org.dash.wallet.features.exploredash.services.UserLocationState import org.dash.wallet.features.exploredash.services.UserLocationStateInt import org.dash.wallet.features.exploredash.utils.DashDirectConfig @@ -109,4 +106,9 @@ abstract class ExploreDashModule { abstract fun bindDataSyncService( exploreDatabase: ExploreDataSyncStatus ): DataSyncStatusService + + @Binds + abstract fun provideDashDirectRepository( + dashDirectRepository: DashDirectRepository + ): DashDirectRepositoryInt } diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt index b481ab4135..91adbf2480 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/interceptor/HeadersInterceptor.kt @@ -19,14 +19,13 @@ package org.dash.wallet.features.exploredash.network.interceptor import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Response -import org.dash.wallet.common.Configuration import org.dash.wallet.features.exploredash.network.service.DashDirectClientConstants import org.dash.wallet.features.exploredash.utils.DashDirectConfig import org.dash.wallet.features.exploredash.utils.DashDirectConstants import javax.inject.Inject class HeadersInterceptor @Inject constructor( - private val config: DashDirectConfig + private val config: DashDirectConfig ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { @@ -35,9 +34,9 @@ class HeadersInterceptor @Inject constructor( requestBuilder.header("Accept", "application/json") requestBuilder.header(DashDirectConstants.CLIENT_ID, DashDirectClientConstants.CLIENT_ID) - val accessToken = runBlocking { config.getPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN) } + val accessToken = runBlocking { config.getPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN) } if (accessToken?.isNotEmpty() == true) { - requestBuilder.header(DashDirectConstants.API_KEY, accessToken) + requestBuilder.header("authentication", "Bearer $accessToken") } requestBuilder.method(original.method, original.body) diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt index 922edd8f0d..d6b227573a 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/network/service/DashDirectServicesApi.kt @@ -27,6 +27,7 @@ interface DashDirectServicesApi { @POST("PurchaseGiftCard") suspend fun purchaseGiftCard( @Header(DashDirectConstants.requestedUUID) deviceID: String, + @Header(DashDirectConstants.EMAIL) email: String, @Body purchaseGiftCardRequest: PurchaseGiftCardRequest ): PurchaseGiftCardResponse? } diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt index 4fa1048c2d..5bdb693beb 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/repository/DashDirectRepository.kt @@ -1,3 +1,4 @@ + /* * Copyright 2021 Dash Core Group. * @@ -48,7 +49,7 @@ class DashDirectRepository @Inject constructor( } override fun isUserSignIn() = - runBlocking { config.getPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN)?.isNotEmpty() ?: false } + runBlocking { config.getPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN)?.isNotEmpty() ?: true } fun reset() { runBlocking { config.setPreference(DashDirectConfig.PREFS_KEY_LAST_DASH_DIRECT_ACCESS_TOKEN, "") } @@ -58,7 +59,8 @@ class DashDirectRepository @Inject constructor( deviceID: String, currency: String, giftCardAmount: Double, - merchantId: Int + merchantId: Long, + userEmail: String ) = safeApiCall { servicesApi.purchaseGiftCard( deviceID = deviceID, @@ -66,13 +68,14 @@ class DashDirectRepository @Inject constructor( currency = currency, giftCardAmount = giftCardAmount, merchantId = merchantId - ) + ), + email = userEmail ) } } interface DashDirectRepositoryInt { suspend fun signIn(email: String, password: String): ResponseResource fun isUserSignIn(): Boolean - suspend fun purchaseGiftCard(deviceID: String, currency: String, giftCardAmount: Double, merchantId: Int): + suspend fun purchaseGiftCard(deviceID: String, currency: String, giftCardAmount: Double, merchantId: Long, userEmail: String): ResponseResource } diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreMapFragment.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreMapFragment.kt index 055f03cc1f..094e4ab50e 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreMapFragment.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreMapFragment.kt @@ -27,6 +27,7 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope +import androidx.navigation.navGraphViewModels import com.bumptech.glide.Glide import com.bumptech.glide.RequestBuilder import com.bumptech.glide.RequestManager @@ -61,7 +62,7 @@ class ExploreMapFragment : SupportMapFragment() { const val DETAILS_ZOOM_LEVEL = 14f } - private val viewModel: ExploreViewModel by activityViewModels() + private val viewModel: ExploreViewModel by navGraphViewModels(R.id.explore_dash) { defaultViewModelProviderFactory } private var savedSearchResultsBounds: LatLngBounds? = null private var savedMerchantLocationsBounds: LatLngBounds? = null private var prevScreenState: ScreenState = ScreenState.SearchResults diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt index 4cce8a0327..c2a4a9cdc0 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/ExploreViewModel.kt @@ -28,8 +28,9 @@ import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.* import kotlinx.coroutines.flow.* +import org.bitcoinj.core.Coin +import org.bitcoinj.utils.Fiat import org.dash.wallet.common.data.Resource -import org.dash.wallet.common.data.ResponseResource import org.dash.wallet.common.data.SingleLiveEvent import org.dash.wallet.common.data.Status import org.dash.wallet.common.livedata.ConnectionLiveData @@ -39,6 +40,7 @@ import org.dash.wallet.features.exploredash.data.ExploreDataSource import org.dash.wallet.features.exploredash.data.model.* import org.dash.wallet.features.exploredash.data.model.GeoBounds import org.dash.wallet.features.exploredash.repository.DashDirectRepository +import org.dash.wallet.features.exploredash.repository.DashDirectRepositoryInt import org.dash.wallet.features.exploredash.repository.DataSyncStatusService import org.dash.wallet.features.exploredash.services.UserLocation import org.dash.wallet.features.exploredash.services.UserLocationStateInt @@ -81,7 +83,7 @@ class ExploreViewModel @Inject constructor( private val locationProvider: UserLocationStateInt, private val syncStatusService: DataSyncStatusService, private val analyticsService: AnalyticsService, - private val repository: DashDirectRepository + private val repository: DashDirectRepositoryInt ) : ViewModel() { companion object { const val QUERY_DEBOUNCE_VALUE = 300L @@ -206,7 +208,6 @@ class ExploreViewModel @Inject constructor( val screenState: LiveData get() = _screenState - // Used for the list of search results private val pagingSearchFlow: Flow> = _searchQuery .debounce(QUERY_DEBOUNCE_VALUE) @@ -274,9 +275,7 @@ class ExploreViewModel @Inject constructor( } } - fun init(exploreTopic: ExploreTopic) { - repository.reset() if (this.exploreTopic != exploreTopic) { clearSearchResults() } @@ -401,14 +400,6 @@ class ExploreViewModel @Inject constructor( suspend fun signInToDashDirect(email: String, password: String) = repository.signIn(email, password) - suspend fun purchaseGiftCard(deviceID: String, currency: String, giftCardAmount: Double, merchantId: Int) = - repository.purchaseGiftCard( - deviceID = deviceID, - giftCardAmount = giftCardAmount, - currency = currency, - merchantId = merchantId - ) - fun onMapMarkerSelected(id: Int) { val item = _allMerchantLocations.value?.firstOrNull { it.id == id } ?: _physicalSearchResults.value?.firstOrNull { it.id == id } diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardFragment.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardFragment.kt new file mode 100644 index 0000000000..3e4167f33a --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardFragment.kt @@ -0,0 +1,166 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.features.exploredash.ui + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.navGraphViewModels +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import org.bitcoinj.core.Coin +import org.bitcoinj.utils.ExchangeRate +import org.bitcoinj.utils.Fiat +import org.dash.wallet.common.ui.OnHideBalanceClickedListener +import org.dash.wallet.common.ui.enter_amount.EnterAmountFragment +import org.dash.wallet.common.ui.enter_amount.EnterAmountViewModel +import org.dash.wallet.common.ui.viewBinding +import org.dash.wallet.common.util.Constants +import org.dash.wallet.common.util.GenericUtils +import org.dash.wallet.features.exploredash.R +import org.dash.wallet.features.exploredash.data.model.Merchant +import org.dash.wallet.features.exploredash.databinding.FragmentPurchaseGiftCardBinding +import org.dash.wallet.features.exploredash.ui.dialogs.PurchaseGiftCardConfirmDialog +import org.slf4j.LoggerFactory + +@FlowPreview +@ExperimentalCoroutinesApi +@AndroidEntryPoint +class PurchaseGiftCardFragment : Fragment(R.layout.fragment_purchase_gift_card) { + companion object { + private val log = LoggerFactory.getLogger(PurchaseGiftCardFragment::class.java) + } + + private val binding by viewBinding(FragmentPurchaseGiftCardBinding::bind) + private val viewModel: PurchaseGiftCardViewModel by activityViewModels() + private var enterAmountFragment: EnterAmountFragment? = null + private val exploreViewModel: ExploreViewModel by navGraphViewModels(R.id.explore_dash) { defaultViewModelProviderFactory } + private val enterAmountViewModel by activityViewModels() + + var selectedMerchent: Merchant? = null + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.titleBar.setNavigationOnClickListener { + findNavController().popBackStack() + } + + if (savedInstanceState == null) { + val fragment = EnterAmountFragment.newInstance( + dashToFiat = false, + showCurrencySelector = false, + isMaxButtonVisible = false, + isCurrencyOptionsPickerVisible = false, + showAmountResultContainer = false, + faitCurrencyCode = if (viewModel.isUserSettingFaitIsNotUSD)Constants.USD_CURRENCY else null + ) + + fragment.setViewDetails(getString(R.string.button_next)) + + childFragmentManager.beginTransaction() + .setReorderingAllowed(true) + .add(R.id.enter_amount_fragment_placeholder, fragment) + .commitNow() + enterAmountFragment = fragment + } + setPaymentHeader() + + enterAmountViewModel.onContinueEvent.observe(viewLifecycleOwner) { + selectedMerchent?.let { selectedMerchant -> + viewModel.purchaseGiftCardDataMerchant = selectedMerchant + viewModel.purchaseGiftCardDataPaymentValue = it + PurchaseGiftCardConfirmDialog().show(requireActivity()) + } + } + + exploreViewModel.selectedItem.value?.let { merchant -> + if (merchant is Merchant && merchant.merchantId != null && !merchant.source.isNullOrEmpty()) { + this.selectedMerchent = merchant + binding.paymentHeaderView.setPaymentAddressViewSubtitle(merchant.name.orEmpty()) + binding.paymentHeaderView.setPaymentAddressViewIcon(merchant.logoLocation) + } + } + + // TODO + val merchantMinimumCardPurchase = 10.00 + val merchantMaximumCardPurchase = 500.00 + + val minFaitValue = Fiat.parseFiat( + Constants.USD_CURRENCY, + merchantMinimumCardPurchase.toString() + ) + + val maxFaitValue = Fiat.parseFiat( + Constants.USD_CURRENCY, + merchantMaximumCardPurchase.toString() + ) + + binding.minValue.text = getString( + R.string.purchase_gift_card_min, + GenericUtils.fiatToString(minFaitValue) + ) + binding.maxValue.text = getString( + R.string.purchase_gift_card_max, + GenericUtils.fiatToString(maxFaitValue) + ) + + viewModel.usdExchangeRate.observe(viewLifecycleOwner) { rate -> + + val exchangeRate = ExchangeRate(Coin.COIN, rate.fiat) + if (viewModel.isUserSettingFaitIsNotUSD) { + viewModel.balance.value?.let { balance -> + updateBalanceLabel(balance, viewModel.usdExchangeRate.value) + } + } + + val minValue = exchangeRate.fiatToCoin(minFaitValue) ?: Coin.ZERO + val maxValue = exchangeRate.fiatToCoin(maxFaitValue) ?: Coin.ZERO + + enterAmountViewModel.setMinAmount(minValue, true) + enterAmountViewModel.setMaxAmount(maxValue) + } + } + + private fun setPaymentHeader() { + binding.paymentHeaderView.setPaymentAddressViewTitle(getString(R.string.explore_option_buy)) + binding.paymentHeaderView.setPaymentAddressViewProposition(getString(R.string.purchase_gift_card_at)) + binding.paymentHeaderView.setOnHideBalanceClickedListener( + onHideBalanceClickedListener = object : OnHideBalanceClickedListener { + override fun onHideBalanceClicked(view: View) { + viewModel.balance.value?.let { balance -> + updateBalanceLabel(balance, viewModel.usdExchangeRate.value) + } + } + } + ) + + viewModel.balance.observe(viewLifecycleOwner) { balance -> + updateBalanceLabel(balance, viewModel.usdExchangeRate.value) + } + } + + private fun updateBalanceLabel(balance: Coin, rate: org.dash.wallet.common.data.ExchangeRate?) { + val exchangeRate = rate?.let { ExchangeRate(Coin.COIN, it.fiat) } + var balanceText = viewModel.dashFormat.format(balance).toString() + exchangeRate?.let { balanceText += " ~ ${GenericUtils.fiatToString(exchangeRate.coinToFiat(balance))}" } + binding.paymentHeaderView.setBalanceValue(balanceText) + } +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardViewModel.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardViewModel.kt new file mode 100644 index 0000000000..0563032c4a --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/PurchaseGiftCardViewModel.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.features.exploredash.ui + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.* +import org.bitcoinj.core.Coin +import org.bitcoinj.utils.Fiat +import org.bitcoinj.utils.MonetaryFormat +import org.dash.wallet.common.Configuration +import org.dash.wallet.common.WalletDataProvider +import org.dash.wallet.common.data.ExchangeRate +import org.dash.wallet.common.data.ResponseResource +import org.dash.wallet.common.services.ExchangeRatesProvider +import org.dash.wallet.common.util.Constants +import org.dash.wallet.features.exploredash.data.model.Merchant +import org.dash.wallet.features.exploredash.data.model.purchase.PurchaseGiftCardResponse +import org.dash.wallet.features.exploredash.repository.DashDirectRepositoryInt +import java.util.* +import javax.inject.Inject + +@HiltViewModel +class PurchaseGiftCardViewModel @Inject constructor( + walletDataProvider: WalletDataProvider, + exchangeRates: ExchangeRatesProvider, + var configuration: Configuration, + private val repository: DashDirectRepositoryInt +) : ViewModel() { + + val isUserSettingFaitIsNotUSD = (configuration.exchangeCurrencyCode != Constants.USD_CURRENCY) + + val dashFormat: MonetaryFormat + get() = configuration.format + + private val _balance = MutableLiveData() + val balance: LiveData + get() = _balance + + private val _exchangeRate: MutableLiveData = MutableLiveData() + val usdExchangeRate: LiveData + get() = _exchangeRate + + var purchaseGiftCardDataMerchant: Merchant? = null + var purchaseGiftCardDataPaymentValue: Pair? = null + + init { + exchangeRates + .observeExchangeRate(Constants.USD_CURRENCY) + .onEach(_exchangeRate::postValue) + .launchIn(viewModelScope) + + walletDataProvider.observeBalance() + .distinctUntilChanged() + .onEach(_balance::postValue) + .launchIn(viewModelScope) + } + + suspend fun purchaseGiftCard(): ResponseResource? { + purchaseGiftCardDataMerchant?.merchantId?.let { + purchaseGiftCardDataPaymentValue?.let { amountValue -> + return repository.purchaseGiftCard( + merchantId = it, + giftCardAmount = amountValue.first.toPlainString().toDouble(), + currency = Constants.DASH_CURRENCY, + deviceID = UUID.randomUUID().toString(), + userEmail = "" + ) + } + } + return null + } +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt index d2bf1435fe..f0f24d2a57 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/SearchFragment.kt @@ -24,7 +24,6 @@ import android.content.Context import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.Log import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.Toast @@ -39,9 +38,9 @@ import androidx.core.view.* import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs +import androidx.navigation.navGraphViewModels import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.bottomsheet.BottomSheetBehavior @@ -84,7 +83,7 @@ class SearchFragment : Fragment(R.layout.fragment_search) { @Inject lateinit var analyticsService: AnalyticsService private val binding by viewBinding(FragmentSearchBinding::bind) - private val viewModel: ExploreViewModel by activityViewModels() + private val viewModel: ExploreViewModel by navGraphViewModels(R.id.explore_dash) { defaultViewModelProviderFactory } private val args by navArgs() private var bottomSheetWasExpanded: Boolean = false @@ -409,9 +408,7 @@ class SearchFragment : Fragment(R.layout.fragment_search) { when (val response = viewModel.signInToDashDirect(email.text.toString(), password.text.toString())) { is ResponseResource.Success -> { if (response.value) { - // TODO open Buy card UI - Toast.makeText(requireContext(), "Open Buy Card", Toast.LENGTH_SHORT).show() - viewModel.logEvent(AnalyticsConstants.Explore.MERCHANT_DETAILS_BUY_GIFT_CARD) + openPurchaseGiftCardFragment() } } @@ -426,6 +423,11 @@ class SearchFragment : Fragment(R.layout.fragment_search) { } } + private fun openPurchaseGiftCardFragment() { + safeNavigate(SearchFragmentDirections.searchToPurchaseGiftCardFragment()) + viewModel.logEvent(AnalyticsConstants.Explore.MERCHANT_DETAILS_BUY_GIFT_CARD) + } + private fun setupSearchInput(bottomSheet: BottomSheetBehavior) { searchHeaderAdapter.setOnSearchQueryChanged { binding.noResultsPanel.isVisible = false @@ -550,7 +552,7 @@ class SearchFragment : Fragment(R.layout.fragment_search) { if (!viewModel.isUserSignInDashDirect()) { showLoginDialog() } else { - Toast.makeText(requireContext(), "Open Buy Card", Toast.LENGTH_SHORT).show() + openPurchaseGiftCardFragment() } } } diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/FiltersDialog.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/FiltersDialog.kt index 45dff356e0..57f549d59e 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/FiltersDialog.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/FiltersDialog.kt @@ -25,6 +25,7 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope +import androidx.navigation.navGraphViewModels import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.* import org.dash.wallet.common.Configuration @@ -58,7 +59,7 @@ class FiltersDialog: OffsetDialogFragment() { private var giftCardPaymentOn: Boolean = true private val binding by viewBinding(DialogFiltersBinding::bind) - private val viewModel: ExploreViewModel by activityViewModels() + private val viewModel: ExploreViewModel by navGraphViewModels(R.id.explore_dash) { defaultViewModelProviderFactory } private var territoriesJob: Deferred>? = null private var radiusOptionsAdapter: RadioGroupAdapter? = null diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/GiftCardDetailsDialog.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/GiftCardDetailsDialog.kt new file mode 100644 index 0000000000..96ec366338 --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/GiftCardDetailsDialog.kt @@ -0,0 +1,129 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.dash.wallet.features.exploredash.ui.dialogs + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.StyleRes +import androidx.core.view.isVisible +import androidx.navigation.fragment.findNavController +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import org.dash.wallet.common.ui.dialogs.OffsetDialogFragment +import org.dash.wallet.common.ui.viewBinding +import org.dash.wallet.common.util.Constants +import org.dash.wallet.common.util.copy +import org.dash.wallet.features.exploredash.R +import org.dash.wallet.features.exploredash.data.model.GiftCardDetailsDialogModel +import org.dash.wallet.features.exploredash.databinding.DialogGiftCardDetailsBinding +import org.dash.wallet.features.exploredash.utils.DashDirectConstants + +@FlowPreview +@ExperimentalCoroutinesApi +@AndroidEntryPoint +class GiftCardDetailsDialog : OffsetDialogFragment() { + @StyleRes + override val backgroundStyle = R.style.PrimaryBackground + override val forceExpand = true + + // private var purchaseGiftCardData: Pair, Merchant>? = null + private val binding by viewBinding(DialogGiftCardDetailsBinding::bind) + + private var giftCardDetailsDialogModel: GiftCardDetailsDialogModel? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + giftCardDetailsDialogModel = it.getParcelable(ARG_MODEL) + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.dialog_gift_card_details, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + giftCardDetailsDialogModel?.let { + binding.merchentName.text = it.merchantName + it.merchantLogo?.let { url -> + Glide.with(requireContext()) + .load(url) + .placeholder(org.dash.wallet.common.R.drawable.ic_image_placeholder) + .error(org.dash.wallet.common.R.drawable.ic_image_placeholder) + .transition(DrawableTransitionOptions.withCrossFade(200)) + .into(binding.merchentLogo) + } + } + + binding.originalPurchaseValue.text = giftCardDetailsDialogModel?.giftCardPrice + + binding.purchaseCardNumber.text = giftCardDetailsDialogModel?.giftCardNumber + binding.purchaseCardPin.text = giftCardDetailsDialogModel?.giftCardPin + + binding.copyCardNumber.setOnClickListener { + binding.purchaseCardNumber.text.toString().copy(requireActivity(), "card number") + } + + binding.copyCardPin.setOnClickListener { + binding.purchaseCardPin.text.toString().copy(requireActivity(), "card pin") + } + + binding.purchaseSeeHowToUseGiftCardLabel.setOnClickListener { + binding.purchaseSeeHowToUseGiftCardLabel.isVisible = false + binding.purchaseSeeHowToUseGiftCard.isVisible = true + } + binding.collapseButton.setOnClickListener { + dismiss() + } + + binding.viewTransactionDetailsCard.setOnClickListener { + giftCardDetailsDialogModel?.transactionId?.let { + if (it.isNotEmpty()) { + findNavController().navigate(Uri.parse("${Constants.DEEP_LINK_PREFIX}/transactions/$it")) + } + } + } + + binding.checkCurrentBalance.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(DashDirectConstants.CHECK_BALANCE_URL)) + requireContext().startActivity(intent) + } + } + + companion object { + private const val ARG_MODEL = "argModel" + + fun newInstance(model: GiftCardDetailsDialogModel) = GiftCardDetailsDialog().apply { + arguments = Bundle().apply { + putParcelable(ARG_MODEL, model) + } + } + } +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/PurchaseGiftCardConfirmDialog.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/PurchaseGiftCardConfirmDialog.kt new file mode 100644 index 0000000000..f0dd82b1c7 --- /dev/null +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/ui/dialogs/PurchaseGiftCardConfirmDialog.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2021 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.dash.wallet.features.exploredash.ui.dialogs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.StyleRes +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.launch +import org.dash.wallet.common.ui.dialogs.OffsetDialogFragment +import org.dash.wallet.common.ui.viewBinding +import org.dash.wallet.common.util.GenericUtils +import org.dash.wallet.features.exploredash.R +import org.dash.wallet.features.exploredash.data.model.GiftCardDetailsDialogModel +import org.dash.wallet.features.exploredash.databinding.DialogConfirmPurchaseGiftCardBinding +import org.dash.wallet.features.exploredash.ui.PurchaseGiftCardViewModel + +@FlowPreview +@ExperimentalCoroutinesApi +@AndroidEntryPoint +class PurchaseGiftCardConfirmDialog : OffsetDialogFragment() { + @StyleRes + override val backgroundStyle = R.style.PrimaryBackground + + private val binding by viewBinding(DialogConfirmPurchaseGiftCardBinding::bind) + private val purchaseGiftCardViewModel: PurchaseGiftCardViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.dialog_confirm_purchase_gift_card, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val merchant = purchaseGiftCardViewModel.purchaseGiftCardDataMerchant + val paymentValue = purchaseGiftCardViewModel.purchaseGiftCardDataPaymentValue + merchant?.let { + binding.merchentName.text = it.name + merchant.logoLocation?.let { + Glide.with(requireContext()) + .load(it) + .placeholder(org.dash.wallet.common.R.drawable.ic_image_placeholder) + .error(org.dash.wallet.common.R.drawable.ic_image_placeholder) + .transition(DrawableTransitionOptions.withCrossFade(200)) + .into(binding.merchentLogo) + } + binding.giftCardDiscountValue.text = "0%" + } + + paymentValue?.let { + binding.giftCardTotalValue.text = + GenericUtils.fiatToString(it.second) + + binding.giftCardYouPayValue.text = + GenericUtils.fiatToString(it.second) + + binding.purchaseCardValue.text = + GenericUtils.fiatToString(it.second) + } + + binding.collapseButton.setOnClickListener { + dismiss() + } + + binding.confirmButton.setOnClickListener { + lifecycleScope.launch { + purchaseGiftCardViewModel.purchaseGiftCard() + // TODO Change to response from API + GiftCardDetailsDialog.newInstance( + GiftCardDetailsDialogModel( + merchantName = merchant?.name, + merchantLogo = merchant?.logoLocation, + giftCardPrice = GenericUtils.fiatToString(paymentValue?.second) + ) + ).show(requireActivity()).also { + val navController = findNavController() + navController.popBackStack(navController.graph.startDestinationId, false) + + this@PurchaseGiftCardConfirmDialog.dismiss() + } + } + } + } +} diff --git a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt index 3c05149020..bcc0120e5a 100644 --- a/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt +++ b/features/exploredash/src/main/java/org/dash/wallet/features/exploredash/utils/DashDirectConstants.kt @@ -21,4 +21,6 @@ object DashDirectConstants { const val API_KEY = "apikey" const val requestedUUID = "requestedUUID" const val CLIENT_ID = "clientId" + const val EMAIL = "email" + const val CHECK_BALANCE_URL = "https://www.lowes.com/l/help/gift-card-balance" } diff --git a/features/exploredash/src/main/res/drawable/ic_dashdirect_logo.xml b/features/exploredash/src/main/res/drawable/ic_dashdirect_logo.xml new file mode 100644 index 0000000000..ccd1f9266a --- /dev/null +++ b/features/exploredash/src/main/res/drawable/ic_dashdirect_logo.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/features/exploredash/src/main/res/drawable/ic_instore_blue.xml b/features/exploredash/src/main/res/drawable/ic_instore_blue.xml new file mode 100644 index 0000000000..71664b27b2 --- /dev/null +++ b/features/exploredash/src/main/res/drawable/ic_instore_blue.xml @@ -0,0 +1,12 @@ + + + + diff --git a/features/exploredash/src/main/res/drawable/ic_online_blue.xml b/features/exploredash/src/main/res/drawable/ic_online_blue.xml new file mode 100644 index 0000000000..7dff975a81 --- /dev/null +++ b/features/exploredash/src/main/res/drawable/ic_online_blue.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/exploredash/src/main/res/drawable/ic_self_chekout_blue.xml b/features/exploredash/src/main/res/drawable/ic_self_chekout_blue.xml new file mode 100644 index 0000000000..befed2a074 --- /dev/null +++ b/features/exploredash/src/main/res/drawable/ic_self_chekout_blue.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/exploredash/src/main/res/layout/dialog_confirm_purchase_gift_card.xml b/features/exploredash/src/main/res/layout/dialog_confirm_purchase_gift_card.xml new file mode 100644 index 0000000000..41e814ff7d --- /dev/null +++ b/features/exploredash/src/main/res/layout/dialog_confirm_purchase_gift_card.xml @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +