Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3117c626 authored by mohit.mali@ftechiz.com's avatar mohit.mali@ftechiz.com
Browse files

Merged changes from master

parent 4ec183e7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ buildscript {
    ext.exoplayer_version = "2.8.1"
    ext.glide_version = "4.8.0"
    ext.junit_version = '4.12'
    ext.kotlin_version = '1.3.31'
    ext.kotlin_version = '1.3.50'
    ext.lifecycle_version = '2.1.0'
    ext.material_version = '1.0.0'
    ext.mockito_version = '2.18.3'
+1 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ android {
        signingConfigs.release.keyAlias = System.getenv("key_alias")
        signingConfigs.release.keyPassword = System.getenv("key_password")
    }

}

androidExtensions {
+2 −2
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
  ~ Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
  ~
  ~ This file is part of QKSMS.
@@ -94,6 +93,7 @@
                android:value=".common.util.QkChooserTargetService" />
        </activity>
        <activity android:name=".feature.settings.SettingsActivity" />
        <activity android:name=".feature.plus.PlusActivity" />
        <activity
            android:name=".feature.gallery.GalleryActivity"
            android:theme="@style/AppTheme.Black" />
+12 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.moez.QKSMS.feature.compose.ComposeActivity
import com.moez.QKSMS.feature.conversationinfo.ConversationInfoActivity
import com.moez.QKSMS.feature.gallery.GalleryActivity
import com.moez.QKSMS.feature.notificationprefs.NotificationPrefsActivity
import com.moez.QKSMS.feature.plus.PlusActivity
import com.moez.QKSMS.feature.scheduled.ScheduledActivity
import com.moez.QKSMS.feature.settings.SettingsActivity
import com.moez.QKSMS.manager.AnalyticsManager
@@ -52,6 +53,7 @@ class Navigator @Inject constructor(
    private val context: Context,
    private val analyticsManager: AnalyticsManager,
    private val notificationManager: NotificationManager,
    private val billingManager: BillingManager,
    private val permissions: PermissionManager
) {

@@ -149,6 +151,16 @@ class Navigator @Inject constructor(
        startActivityExternal(intent)
    }

    fun showFork() {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/moezbhatti/qksms"))
        startActivity(intent)
    }

    fun showCopyright() {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://gitlab.e.foundation/e/apps/message/blob/e-features/AUTHORS"))
        startActivity(intent)
    }

    fun showChangelog() {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/moezbhatti/qksms/releases"))
        startActivityExternal(intent)
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
 *
 * This file is part of QKSMS.
 *
 * QKSMS 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.
 *
 * QKSMS 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 QKSMS.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.moez.QKSMS.common.util

import android.app.Activity
import android.content.Context
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClient.BillingResponse
import com.android.billingclient.api.BillingClient.SkuType
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingFlowParams
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.SkuDetails
import com.android.billingclient.api.SkuDetailsParams
import com.moez.QKSMS.BuildConfig
import com.moez.QKSMS.manager.AnalyticsManager
import io.reactivex.Flowable
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class BillingManager @Inject constructor(
        context: Context,
        private val analyticsManager: AnalyticsManager
) : PurchasesUpdatedListener {

    companion object {
        const val SKU_PLUS = "remove_ads"
        const val SKU_PLUS_DONATE = "qksms_plus_donate"
    }

    val products: Observable<List<SkuDetails>> = BehaviorSubject.create()
    val upgradeStatus: Observable<Boolean>

    private val skus = listOf(SKU_PLUS, SKU_PLUS_DONATE)
    private val purchaseListObservable = BehaviorSubject.create<List<Purchase>>()

    private val billingClient: BillingClient = BillingClient.newBuilder(context).setListener(this).build()
    private var isServiceConnected = false

    init {
        startServiceConnection {
            queryPurchases()
            querySkuDetailsAsync()
        }

        upgradeStatus = when (BuildConfig.FLAVOR) {
            "noAnalytics" -> BehaviorSubject.createDefault(true)

            else -> purchaseListObservable
                    .map { purchases -> purchases.any { it.sku == SKU_PLUS } || purchases.any { it.sku == SKU_PLUS_DONATE } }
                    .doOnNext { upgraded -> analyticsManager.setUserProperty("Upgraded", upgraded) }
        }
    }

    private fun queryPurchases() {
        executeServiceRequest {
            // Load the cached data
            purchaseListObservable.onNext(billingClient.queryPurchases(SkuType.INAPP).purchasesList.orEmpty())

            // On a fresh device, the purchase might not be cached, and so we'll need to force a refresh
            billingClient.queryPurchaseHistoryAsync(SkuType.INAPP) { _, _ ->
                purchaseListObservable.onNext(billingClient.queryPurchases(SkuType.INAPP).purchasesList.orEmpty())
            }
        }
    }


    private fun startServiceConnection(onSuccess: () -> Unit) {
        val listener = object : BillingClientStateListener {
            override fun onBillingSetupFinished(@BillingResponse billingResponseCode: Int) {
                if (billingResponseCode == BillingResponse.OK) {
                    isServiceConnected = true
                    onSuccess()
                } else {
                    Timber.w("Billing response: $billingResponseCode")
                    purchaseListObservable.onNext(listOf())
                }
            }

            override fun onBillingServiceDisconnected() {
                isServiceConnected = false
            }
        }

        Flowable.fromCallable { billingClient.startConnection(listener) }
                .subscribeOn(Schedulers.io())
                .subscribe()
    }

    private fun querySkuDetailsAsync() {
        executeServiceRequest {
            val subParams = SkuDetailsParams.newBuilder().setSkusList(skus).setType(BillingClient.SkuType.INAPP)
            billingClient.querySkuDetailsAsync(subParams.build()) { responseCode, skuDetailsList ->
                if (responseCode == BillingResponse.OK) {
                    (products as Subject).onNext(skuDetailsList)
                }
            }
        }
    }

    fun initiatePurchaseFlow(activity: Activity, sku: String) {
        executeServiceRequest {
            val params = BillingFlowParams.newBuilder().setSku(sku).setType(SkuType.INAPP)
            billingClient.launchBillingFlow(activity, params.build())
        }
    }

    private fun executeServiceRequest(runnable: () -> Unit) {
        when (isServiceConnected) {
            true -> runnable()
            false -> startServiceConnection(runnable)
        }
    }

    override fun onPurchasesUpdated(resultCode: Int, purchases: List<Purchase>?) {
        if (resultCode == BillingResponse.OK) {
            purchaseListObservable.onNext(purchases.orEmpty())
        }
    }

}
Loading