Skip to content

Commit

Permalink
Add license flavor dimension and split app into 2 versions "Proprieta…
Browse files Browse the repository at this point in the history
…ry" and "NoProprietary"
  • Loading branch information
httpdispatch committed Apr 15, 2021
1 parent a274b88 commit c9ed186
Show file tree
Hide file tree
Showing 41 changed files with 629 additions and 254 deletions.
44 changes: 35 additions & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ val propsFile = rootProject.file("keystore.properties")
fun getVersionCode(minSdkVersion: Int) =
2000000000 + versionMajor * 10000000 + versionMinor * 100000 + versionPatch * 1000 + versionBuild * 100 + minSdkVersion

val proprietaryConfigurations = listOf(
"notificationListenerV18ProprietaryImplementation",
"notificationListenerV27ProprietaryImplementation",
"accessibilityV14ProprietaryImplementation",
"accessibilityV27ProprietaryImplementation"
)

android {
compileSdkVersion(Constants.COMPILE_SDK_VERSION)
buildToolsVersion = Constants.BUILD_TOOLS_VERSION
Expand Down Expand Up @@ -113,15 +120,15 @@ android {
}
}
}
flavorDimensions("service", "api")
flavorDimensions("service", "api", "license")
productFlavors {
create("accessibility") {
dimension = "service"
versionCode = 0
versionCode = 1
}
create("notificationListener") {
dimension = "service"
versionCode = 1
versionCode = 2
}
create("v14") {
minSdkVersion(14)
Expand All @@ -138,6 +145,26 @@ android {
dimension = "api"
versionCode = 3
}
create("Proprietary") {
dimension = "license"
}
create("NoProprietary") {
dimension = "license"
}
}


configurations {
proprietaryConfigurations.forEach { create(it) }
}

sourceSets {
create("notificationListenerV27Proprietary") {
manifest.srcFile("src/notificationListenerV27/AndroidManifest.xml")
}
create("notificationListenerV27NoProprietary") {
manifest.srcFile("src/notificationListenerV27/AndroidManifest.xml")
}
}

lintOptions {
Expand Down Expand Up @@ -176,11 +203,6 @@ android {
}
}

configurations.onEach {
it.resolutionStrategy {
}
}

dependencies {
implementation(kotlin("stdlib-jdk7", KotlinCompilerVersion.VERSION))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.COROUTINES}")
Expand All @@ -199,7 +221,6 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-livedata-ktx:${Versions.LIFECYCLE}")
implementation("androidx.lifecycle:lifecycle-service:${Versions.LIFECYCLE}")
implementation("com.google.android.material:material:1.3.0")
implementation("com.android.billingclient:billing:3.0.2")
implementation("androidx.navigation:navigation-fragment-ktx:${Versions.NAVIGATION}")
implementation("androidx.navigation:navigation-ui-ktx:${Versions.NAVIGATION}")

Expand Down Expand Up @@ -234,6 +255,11 @@ dependencies {
implementation("androidx.work:work-runtime:${Versions.WORK}")
implementation("androidx.work:work-runtime-ktx:${Versions.WORK}")

val proprietaryDependencies = listOf("com.android.billingclient:billing:3.0.2")
proprietaryConfigurations.forEach { configuration ->
proprietaryDependencies.forEach { dependency -> add(configuration, dependency) }
}

androidTestImplementation("junit:junit:4.13")
androidTestImplementation("androidx.test.espresso:espresso-core:3.3.0")
androidTestImplementation("androidx.test:runner:1.3.0")
Expand Down
6 changes: 5 additions & 1 deletion app/lint.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
<lint>

<!-- Ignore the UnusedResources issue in the given ids -->
<issue id="UnusedResources" severity="ignore">
<ignore
regexp="payment_error_service_unavailable|payment_error_user_canceled|payment_error_purchase_pending" />
</issue>
<issue id="OverrideAbstract" severity="ignore">
<ignore regexp="ReminderNotificationListenerService" />
</issue>
</lint>
</lint>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name" translatable="false">Missed Notifications Reminder Av14NP Debug</string>
<string name="launcher_name" translatable="false">Missed Notifications Reminder Av14NP (D)</string>

</resources>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name" translatable="false">Missed Notifications Reminder Av27 Debug</string>
<string name="launcher_name" translatable="false">Missed Notifications Reminder Av27 (D)</string>
<string name="app_name" translatable="false">Missed Notifications Reminder Av14P Debug</string>
<string name="launcher_name" translatable="false">Missed Notifications Reminder Av14P (D)</string>

</resources>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name" translatable="false">Missed Notifications Reminder Av27NP Debug</string>
<string name="launcher_name" translatable="false">Missed Notifications Reminder Av27NP (D)</string>

</resources>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name" translatable="false">Missed Notifications Reminder v18 Debug</string>
<string name="launcher_name" translatable="false">Missed Notifications Reminder v18 (D)</string>
<string name="app_name" translatable="false">Missed Notifications Reminder Av27P Debug</string>
<string name="launcher_name" translatable="false">Missed Notifications Reminder Av27P (D)</string>

</resources>
</resources>
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.app.missednotificationsreminder.data
package com.app.missednotificationsreminder.common.domain.entities

import com.app.missednotificationsreminder.data.ResultWrapper.Error
import com.app.missednotificationsreminder.data.ResultWrapper.Success
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.fold
import kotlinx.coroutines.flow.transformWhile
import com.app.missednotificationsreminder.common.domain.entities.ResultWrapper.Error
import com.app.missednotificationsreminder.common.domain.entities.ResultWrapper.Success

/**
* A generic class that holds a value or error
Expand All @@ -13,7 +10,11 @@ import kotlinx.coroutines.flow.transformWhile
sealed class ResultWrapper<out R> {

data class Success<out T>(val data: T) : ResultWrapper<T>()
data class Error(val throwable: Throwable? = null, val code: Int = 0, val message: String? = null) :
data class Error(
val throwable: Throwable? = null,
val code: Int = 0,
val message: String? = null
) :
ResultWrapper<Nothing>() {
fun messageOrDefault(defaultValue: () -> String): String = message ?: defaultValue()
}
Expand All @@ -35,7 +36,8 @@ val ResultWrapper<*>.succeeded
/**
* Map [Result] to [ResultWrapper]
*/
fun <T> Result<T>.asResultWrapper(): ResultWrapper<T> = if (isFailure) Error(exceptionOrNull()) else Success<T>(getOrNull()!!)
fun <T> Result<T>.asResultWrapper(): ResultWrapper<T> =
if (isFailure) Error(exceptionOrNull()) else Success<T>(getOrNull()!!)

/**
* Returns the encapsulated result of the given [transform] function applied to the encapsulated value
Expand Down Expand Up @@ -116,40 +118,3 @@ inline fun <R, T> ResultWrapper<T>.fold(
is Error -> onFailure(this@fold)
}
}

/**
* Collect the `Flow` results until last or [Error] value is emitted. Collect
* data using [collector].
*
* Note, that this function rethrows any [Throwable] exception thrown by [collector] function.
*/
suspend fun <T, R> Flow<ResultWrapper<T>>.collectWithLastErrorOrSuccessStatusSimple(
defaultValue: ResultWrapper<R>,
collector: (R, T) -> R): ResultWrapper<R> {
return collectWithLastErrorOrSuccessStatus(
defaultValue,
{ it.succeeded })
{ mergedValue, value ->
value.map {
val mergedData = (mergedValue as Success<R>).data
collector(mergedData, it)
}
}
}

/**
* Collect the `Flow` results until last or not succeeded value is emitted. Collect
* data using [collector].
*
* Note, that this function rethrows any [Throwable] exception thrown by [collector] function.
*/
suspend fun <T, R> Flow<T>.collectWithLastErrorOrSuccessStatus(
defaultValue: R,
succeededTest: (T) -> Boolean,
collector: suspend (R, T) -> R): R {
return transformWhile {
emit(it)
succeededTest(it)
}
.fold(defaultValue, collector)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.lifecycle.*
import androidx.recyclerview.widget.RecyclerView
import com.app.missednotificationsreminder.data.onSuccess
import com.app.missednotificationsreminder.common.domain.entities.onSuccess
import com.app.missednotificationsreminder.databinding.ItemContributeBinding
import com.app.missednotificationsreminder.databinding.ItemDonateBinding
import com.app.missednotificationsreminder.ui.widget.recyclerview.LifecycleAdapterWithViewEffect
Expand Down Expand Up @@ -115,4 +115,4 @@ class PurchaseAdapter @Inject constructor(private val purchaseViewState: StateFl
binding.viewState = item.asLiveData()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.app.missednotificationsreminder.payment

import com.android.billingclient.api.SkuDetails
import com.app.missednotificationsreminder.payment.billing.domain.entities.SkuDetails

/**
* The class to store purchase item information
*/
data class PurchaseItem(val skuDetails: SkuDetails)
data class PurchaseItem(val skuDetails: SkuDetails)
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.app.missednotificationsreminder.payment

import com.android.billingclient.api.SkuDetails
import com.app.missednotificationsreminder.payment.billing.domain.entities.SkuDetails

/**
* The class to store purchase item view state information
*/
data class PurchaseItemViewState(val skuDetails: SkuDetails) {
val price: String = skuDetails.price
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.app.missednotificationsreminder.payment

import com.android.billingclient.api.SkuDetails
import com.app.missednotificationsreminder.payment.billing.domain.entities.SkuDetails

sealed class PurchaseViewEffect {
/**
Expand All @@ -12,4 +12,4 @@ sealed class PurchaseViewEffect {
* Show the specified [message]
*/
data class Message(val message: String) : PurchaseViewEffect()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ package com.app.missednotificationsreminder.payment

import android.app.Activity
import androidx.lifecycle.viewModelScope
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.SkuDetails
import com.app.missednotificationsreminder.R
import com.app.missednotificationsreminder.binding.model.BaseViewStateViewEffectModel
import com.app.missednotificationsreminder.binding.util.bindWithPreferences
import com.app.missednotificationsreminder.data.*
import com.app.missednotificationsreminder.common.domain.entities.*
import com.app.missednotificationsreminder.data.source.ResourceDataSource
import com.app.missednotificationsreminder.payment.billing.data.source.BillingErrorCodes
import com.app.missednotificationsreminder.payment.billing.data.source.PurchaseRepository
import com.app.missednotificationsreminder.payment.data.model.Purchase
import com.app.missednotificationsreminder.payment.billing.domain.entities.BillingErrorCodes
import com.app.missednotificationsreminder.payment.billing.domain.entities.SkuDetails
import com.app.missednotificationsreminder.payment.billing.domain.entities.SkuType
import com.app.missednotificationsreminder.payment.billing.domain.repository.PurchaseRepository
import com.app.missednotificationsreminder.payment.di.qualifiers.AvailableSkus
import com.app.missednotificationsreminder.payment.model.Purchase
import com.app.missednotificationsreminder.util.loadingstate.HasLoadingStateManager
import com.app.missednotificationsreminder.util.loadingstate.LoadingState
import com.app.missednotificationsreminder.util.loadingstate.LoadingStateManager
import com.tfcporciuncula.flow.Preference
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
Expand All @@ -28,16 +27,15 @@ import kotlin.time.ExperimentalTime
/**
* The view model for the applications selection view
*/
@ExperimentalCoroutinesApi
@OptIn(ExperimentalTime::class)
class PurchaseViewModel @Inject constructor(
@param:AvailableSkus private val skus: List<String>,
private val purchaseRepository: PurchaseRepository,
private val purchases: Preference<List<Purchase>>,
private val resourcesDataSource: ResourceDataSource,
) :
BaseViewStateViewEffectModel<PurchaseViewState, PurchaseViewEffect, PurchaseViewStatePartialChanges>(
PurchaseViewState(contributeOptions = resourcesDataSource.getString(R.string.contribution_contribute_options))),
PurchaseViewState(contributeOptions = resourcesDataSource.getString(R.string.contribution_contribute_options))
),
ObservesPendingPayments by ObservesPendingPaymentsImpl(purchaseRepository, purchases),
HasLoadingStateManager {
init {
Expand All @@ -55,8 +53,7 @@ class PurchaseViewModel @Inject constructor(
override var loadingState: LoadingState
get() = viewState.value.loadingState
set(value) {
processSync(PurchaseViewStatePartialChanges.LoadingStateChange(
value))
processSync(PurchaseViewStatePartialChanges.LoadingStateChange(value))
}

}
Expand All @@ -80,7 +77,7 @@ class PurchaseViewModel @Inject constructor(
attachLoadingStatus(resourcesDataSource.getString(R.string.payment_loading_purchase_items)) {
purchaseRepository.verifyAndConsumePendingPurchases()
.also { purchaseCompleted(it.skuDetails) }
purchaseRepository.getSkuDetails(skus, BillingClient.SkuType.INAPP)
purchaseRepository.getSkuDetails(skus, SkuType.INAPP)
.map { skuDetails ->
skuDetails
.asSequence()
Expand Down Expand Up @@ -111,10 +108,16 @@ class PurchaseViewModel @Inject constructor(
.operationStatus
}
.fold(
{
requestViewEffect(PurchaseViewEffect.Message(resourcesDataSource.getString(R.string.payment_purchase_done)))
onSuccess = {
requestViewEffect(
PurchaseViewEffect.Message(
resourcesDataSource.getString(
R.string.payment_purchase_done
)
)
)
},
{ error ->
onFailure = { error ->
if (canRetryPendingPayments(error)) {
viewModelScope.launch { observePendingPayments() }
}
Expand All @@ -129,15 +132,20 @@ class PurchaseViewModel @Inject constructor(

@ExperimentalTime
interface ObservesPendingPayments {
suspend fun observePendingPayments(initialDelay: Long = DurationUnit.MINUTES.toMillis(3), interval: Long = DurationUnit.MINUTES.toMillis(3))
suspend fun observePendingPayments(
initialDelay: Long = DurationUnit.MINUTES.toMillis(3),
interval: Long = DurationUnit.MINUTES.toMillis(3)
)

fun canRetryPendingPayments(error: ResultWrapper.Error): Boolean
fun purchaseCompleted(purchasedGoods: List<SkuDetails>)
}

@ExperimentalTime
class ObservesPendingPaymentsImpl(
private val purchaseRepository: PurchaseRepository,
private val purchases: Preference<List<Purchase>>) : ObservesPendingPayments {
private val purchases: Preference<List<Purchase>>
) : ObservesPendingPayments {
override suspend fun observePendingPayments(initialDelay: Long, interval: Long) {
Timber.d("observePendingPayments() called with: initialDelay = $initialDelay, interval = $interval")
delay(initialDelay)
Expand Down Expand Up @@ -169,4 +177,4 @@ class ObservesPendingPaymentsImpl(
.takeIf { it.isNotEmpty() }
?.run { purchases.set(purchases.get() + this@run) }
}
}
}
Loading

0 comments on commit c9ed186

Please sign in to comment.