Add billing module and include in play implementation.
This commit is contained in:
parent
82443af8f7
commit
cda029cd93
17 changed files with 375 additions and 3 deletions
|
@ -558,6 +558,8 @@ dependencies {
|
|||
implementation(libs.rxjava3.rxkotlin)
|
||||
implementation(libs.rxdogtag)
|
||||
|
||||
"playImplementation"(project(":billing"))
|
||||
|
||||
"spinnerImplementation"(project(":spinner"))
|
||||
|
||||
"canaryImplementation"(libs.square.leakcanary)
|
||||
|
|
|
@ -196,6 +196,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||
.addNonBlocking(this::beginJobLoop)
|
||||
.addNonBlocking(EmojiSource::refresh)
|
||||
.addNonBlocking(() -> AppDependencies.getGiphyMp4Cache().onAppStart(this))
|
||||
.addNonBlocking(AppDependencies::getBillingApi)
|
||||
.addNonBlocking(this::ensureProfileUploaded)
|
||||
.addNonBlocking(() -> AppDependencies.getExpireStoriesManager().scheduleIfNecessary())
|
||||
.addPostRender(() -> AppDependencies.getDeletedCallEventManager().scheduleIfNecessary())
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.billing
|
||||
|
||||
/**
|
||||
* Variant interface for the BillingApi.
|
||||
*/
|
||||
interface GooglePlayBillingApi {
|
||||
fun isApiAvailable(): Boolean = false
|
||||
suspend fun queryProducts() {}
|
||||
|
||||
/**
|
||||
* Empty implementation, to be used when play services are available but
|
||||
* GooglePlayBillingApi is not available.
|
||||
*/
|
||||
object Empty : GooglePlayBillingApi
|
||||
}
|
|
@ -12,6 +12,7 @@ import org.signal.core.util.resettableLazy
|
|||
import org.signal.libsignal.net.Network
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations
|
||||
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations
|
||||
import org.thoughtcrime.securesms.billing.GooglePlayBillingApi
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender
|
||||
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl
|
||||
|
@ -210,6 +211,11 @@ object AppDependencies {
|
|||
provider.provideAndroidCallAudioManager()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val billingApi: GooglePlayBillingApi by lazy {
|
||||
provider.provideBillingApi()
|
||||
}
|
||||
|
||||
private val _webSocketObserver: Subject<WebSocketConnectionState> = BehaviorSubject.create()
|
||||
|
||||
/**
|
||||
|
@ -342,5 +348,6 @@ object AppDependencies {
|
|||
fun provideClientZkReceiptOperations(signalServiceConfiguration: SignalServiceConfiguration): ClientZkReceiptOperations
|
||||
fun provideScheduledMessageManager(): ScheduledMessageManager
|
||||
fun provideLibsignalNetwork(config: SignalServiceConfiguration): Network
|
||||
fun provideBillingApi(): GooglePlayBillingApi
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ import org.signal.libsignal.net.Network;
|
|||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.billing.GooglePlayBillingApi;
|
||||
import org.thoughtcrime.securesms.billing.GooglePlayBillingFactory;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock;
|
||||
|
@ -69,8 +71,8 @@ import org.thoughtcrime.securesms.util.AlarmSleepTimer;
|
|||
import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
||||
import org.thoughtcrime.securesms.util.ByteUnit;
|
||||
import org.thoughtcrime.securesms.util.EarlyMessageCache;
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig;
|
||||
import org.thoughtcrime.securesms.util.FrameRateTracker;
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache;
|
||||
import org.thoughtcrime.securesms.video.exo.SimpleExoPlayerPool;
|
||||
|
@ -92,11 +94,11 @@ import org.whispersystems.signalservice.api.util.SleepTimer;
|
|||
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketFactory;
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||
import org.whispersystems.signalservice.internal.websocket.LibSignalChatConnection;
|
||||
import org.whispersystems.signalservice.internal.websocket.LibSignalNetworkExtensions;
|
||||
import org.whispersystems.signalservice.internal.websocket.OkHttpWebSocketConnection;
|
||||
import org.whispersystems.signalservice.internal.websocket.ShadowingWebSocketConnection;
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection;
|
||||
import org.whispersystems.signalservice.internal.websocket.LibSignalChatConnection;
|
||||
import org.whispersystems.signalservice.internal.websocket.OkHttpWebSocketConnection;
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketShadowingBridge;
|
||||
|
||||
import java.util.Optional;
|
||||
|
@ -457,6 +459,11 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider {
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GooglePlayBillingApi provideBillingApi() {
|
||||
return GooglePlayBillingFactory.create(context);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class DynamicCredentialsProvider implements CredentialsProvider {
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.thoughtcrime.securesms.maps;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
|
@ -73,6 +74,7 @@ class LocationRetriever implements DefaultLifecycleObserver, LocationListener {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public void onStop(@NonNull LifecycleOwner owner) {
|
||||
Log.i(TAG, "Removing any possible location listeners.");
|
||||
|
|
28
app/src/play/AndroidManifest.xml
Normal file
28
app/src/play/AndroidManifest.xml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"/>
|
||||
|
||||
<application>
|
||||
<receiver android:name=".apkupdate.ApkUpdateRefreshListener" android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".apkupdate.ApkUpdateDownloadManagerReceiver" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".apkupdate.ApkUpdatePackageInstallerReceiver"
|
||||
android:exported="true" />
|
||||
|
||||
<receiver
|
||||
android:name=".apkupdate.ApkUpdateNotificationReceiver"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,41 @@
|
|||
package org.thoughtcrime.securesms.billing
|
||||
|
||||
import android.content.Context
|
||||
import com.android.billingclient.api.ProductDetailsResult
|
||||
import org.signal.billing.BillingApi
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
|
||||
/**
|
||||
* Play billing factory. Returns empty implementation if message backups are not enabled.
|
||||
*/
|
||||
object GooglePlayBillingFactory {
|
||||
@JvmStatic
|
||||
fun create(context: Context): GooglePlayBillingApi {
|
||||
return if (RemoteConfig.messageBackups) {
|
||||
GooglePlayBillingApiImpl(context)
|
||||
} else {
|
||||
GooglePlayBillingApi.Empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play Store implementation
|
||||
*/
|
||||
private class GooglePlayBillingApiImpl(context: Context) : GooglePlayBillingApi {
|
||||
|
||||
private companion object {
|
||||
val TAG = Log.tag(GooglePlayBillingApiImpl::class)
|
||||
}
|
||||
|
||||
private val billingApi: BillingApi = BillingApi.getOrCreate(context)
|
||||
|
||||
override fun isApiAvailable(): Boolean = billingApi.areSubscriptionsSupported()
|
||||
|
||||
override suspend fun queryProducts() {
|
||||
val products: ProductDetailsResult = billingApi.queryProducts()
|
||||
|
||||
Log.d(TAG, "queryProducts: ${products.billingResult.responseCode}, ${products.billingResult.debugMessage}")
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import org.signal.core.util.concurrent.DeadlockDetector;
|
|||
import org.signal.libsignal.net.Network;
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations;
|
||||
import org.thoughtcrime.securesms.billing.GooglePlayBillingApi;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl;
|
||||
|
@ -238,4 +239,9 @@ public class MockApplicationDependencyProvider implements AppDependencies.Provid
|
|||
public @NonNull Network provideLibsignalNetwork(@NonNull SignalServiceConfiguration config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GooglePlayBillingApi provideBillingApi() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package org.thoughtcrime.securesms.billing
|
||||
|
||||
import android.content.Context
|
||||
|
||||
/**
|
||||
* Website builds do not support google play billing.
|
||||
*/
|
||||
object GooglePlayBillingFactory {
|
||||
@JvmStatic
|
||||
fun create(context: Context): GooglePlayBillingApi {
|
||||
return GooglePlayBillingApi.Empty
|
||||
}
|
||||
}
|
14
billing/build.gradle.kts
Normal file
14
billing/build.gradle.kts
Normal file
|
@ -0,0 +1,14 @@
|
|||
plugins {
|
||||
id("signal-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.signal.billing"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
lintChecks(project(":lintchecks"))
|
||||
|
||||
api(libs.android.billing)
|
||||
implementation(project(":core-util"))
|
||||
}
|
5
billing/src/main/AndroidManifest.xml
Normal file
5
billing/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
169
billing/src/main/java/org/signal/billing/BillingApi.kt
Normal file
169
billing/src/main/java/org/signal/billing/BillingApi.kt
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.billing
|
||||
|
||||
import android.content.Context
|
||||
import com.android.billingclient.api.BillingClient
|
||||
import com.android.billingclient.api.BillingClient.BillingResponseCode
|
||||
import com.android.billingclient.api.BillingClientStateListener
|
||||
import com.android.billingclient.api.BillingResult
|
||||
import com.android.billingclient.api.PendingPurchasesParams
|
||||
import com.android.billingclient.api.ProductDetailsResult
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener
|
||||
import com.android.billingclient.api.QueryProductDetailsParams
|
||||
import com.android.billingclient.api.queryProductDetails
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.retry
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.signal.core.util.logging.Log
|
||||
|
||||
/**
|
||||
* BillingApi serves as the core location for interacting with the Google Billing API. Use of this API is required
|
||||
* for remote backups paid tier, and will only be available in play store builds.
|
||||
*
|
||||
* Care should be taken here to ensure only one instance of this exists at a time.
|
||||
*/
|
||||
class BillingApi private constructor(
|
||||
context: Context
|
||||
) {
|
||||
companion object {
|
||||
private val TAG = Log.tag(BillingApi::class)
|
||||
|
||||
private var instance: BillingApi? = null
|
||||
|
||||
@Synchronized
|
||||
fun getOrCreate(context: Context): BillingApi {
|
||||
return instance ?: BillingApi(context).let {
|
||||
instance = it
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val connectionState = MutableStateFlow<State>(State.Init)
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
private val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
|
||||
Log.d(TAG, "purchasesUpdatedListener: ${billingResult.responseCode}")
|
||||
Log.d(TAG, "purchasesUpdatedListener: Detected ${purchases?.size ?: 0} purchases.")
|
||||
}
|
||||
|
||||
private val billingClient: BillingClient = BillingClient.newBuilder(context)
|
||||
.setListener(purchasesUpdatedListener)
|
||||
.enablePendingPurchases(
|
||||
PendingPurchasesParams.newBuilder()
|
||||
.enableOneTimeProducts()
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
createConnectionFlow()
|
||||
.retry { it is RetryException } // TODO [message-backups] - consider a delay here
|
||||
.collect { newState ->
|
||||
Log.d(TAG, "Updating Google Play Billing connection state: $newState")
|
||||
connectionState.update {
|
||||
newState
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun queryProducts(): ProductDetailsResult {
|
||||
val productList = listOf(
|
||||
QueryProductDetailsParams.Product.newBuilder()
|
||||
.setProductId("") // TODO [message-backups] where does the product id come from?
|
||||
.setProductType(BillingClient.ProductType.SUBS)
|
||||
.build()
|
||||
)
|
||||
|
||||
val params = QueryProductDetailsParams.newBuilder()
|
||||
.setProductList(productList)
|
||||
.build()
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
doOnConnectionReady {
|
||||
billingClient.queryProductDetails(params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not subscriptions are supported by a user's device. Lack of subscription support is generally due
|
||||
* to out-of-date Google Play API
|
||||
*/
|
||||
fun areSubscriptionsSupported(): Boolean {
|
||||
return billingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS).responseCode == BillingResponseCode.OK
|
||||
}
|
||||
|
||||
private suspend fun <T> doOnConnectionReady(block: suspend () -> T): T {
|
||||
val state = connectionState
|
||||
.filter { it == State.Connected || it is State.Failure }
|
||||
.first()
|
||||
|
||||
return when (state) {
|
||||
State.Connected -> block()
|
||||
is State.Failure -> throw state.billingError
|
||||
else -> error("Unexpected state: $state")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createConnectionFlow(): Flow<State> {
|
||||
return callbackFlow {
|
||||
Log.d(TAG, "Starting Google Play Billing connection...", true)
|
||||
trySend(State.Connecting)
|
||||
|
||||
billingClient.startConnection(object : BillingClientStateListener {
|
||||
override fun onBillingServiceDisconnected() {
|
||||
Log.d(TAG, "Google Play Billing became disconnected.", true)
|
||||
trySend(State.Disconnected)
|
||||
cancel(CancellationException("Google Play Billing became disconnected.", RetryException()))
|
||||
}
|
||||
|
||||
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
||||
Log.d(TAG, "onBillingSetupFinished: ${billingResult.responseCode}")
|
||||
if (billingResult.responseCode == BillingResponseCode.OK) {
|
||||
Log.d(TAG, "Google Play Billing is ready.", true)
|
||||
trySend(State.Connected)
|
||||
} else {
|
||||
Log.d(TAG, "Google Play Billing failed to connect.", true)
|
||||
val billingError = BillingError(
|
||||
billingResponseCode = billingResult.responseCode
|
||||
)
|
||||
trySend(State.Failure(billingError))
|
||||
cancel(CancellationException("Failed to connect to Google Play Billing", billingError))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
awaitClose {
|
||||
billingClient.endConnection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed interface State {
|
||||
data object Init : State
|
||||
data object Connecting : State
|
||||
data object Connected : State
|
||||
data object Disconnected : State
|
||||
data class Failure(val billingError: BillingError) : State
|
||||
}
|
||||
|
||||
private class RetryException : Exception()
|
||||
}
|
10
billing/src/main/java/org/signal/billing/BillingError.kt
Normal file
10
billing/src/main/java/org/signal/billing/BillingError.kt
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.billing
|
||||
|
||||
class BillingError(
|
||||
val billingResponseCode: Int
|
||||
) : Exception()
|
|
@ -99,6 +99,7 @@ dependencyResolutionManagement {
|
|||
library("androidx-asynclayoutinflater-appcompat", "androidx.asynclayoutinflater:asynclayoutinflater-appcompat:1.1.0-alpha01")
|
||||
library("androidx-emoji2", "androidx.emoji2:emoji2:1.4.0")
|
||||
library("androidx-documentfile", "androidx.documentfile:documentfile:1.0.0")
|
||||
library("android-billing", "com.android.billingclient:billing-ktx:7.0.0")
|
||||
|
||||
// Material
|
||||
library("material-material", "com.google.android.material:material:1.8.0")
|
||||
|
|
|
@ -3396,6 +3396,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="81dd485618a509a3235929b9eb13091d884452661de6ce5a45cc38b1c555421c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.android.billingclient" name="billing" version="7.0.0">
|
||||
<artifact name="billing-7.0.0.aar">
|
||||
<sha256 value="7d58671c3e56da57befe2798fa60f806a26bb724d326a0865da05bc50827ff87" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.android.billingclient" name="billing-ktx" version="7.0.0">
|
||||
<artifact name="billing-ktx-7.0.0.aar">
|
||||
<sha256 value="bfb7416b270b2a15ddb09fe0c3a9f9a4edeadb348c875104c615dce9766924ba" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.android.databinding" name="baseLibrary" version="8.0.2">
|
||||
<artifact name="baseLibrary-8.0.2.jar">
|
||||
<sha256 value="530b2113317ff4d0f69ffdfb49387ba4b86aac169e1c77dff943405b79adcf8b" origin="Generated by Gradle"/>
|
||||
|
@ -4871,6 +4881,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="2896d76f432be52167295bb9ce45ade25c310aeffc04d28cf8db6a15868e83de" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-base" version="18.3.0">
|
||||
<artifact name="play-services-base-18.3.0.aar">
|
||||
<sha256 value="94066a46047e3d593eb652383e7767e0630385ecd75133e68c21124aa96b8dc2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-basement" version="18.0.0">
|
||||
<artifact name="play-services-basement-18.0.0.aar">
|
||||
<sha256 value="55c1777467901a2d399f3252384c4976284aa35fddfd5995466dbeacb49f9dd6" origin="Generated by Gradle"/>
|
||||
|
@ -4886,6 +4901,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="ef43ebfc641d71481543524f46d126793b4cb57bf466c4df4ce43d0cb5e11b91" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-basement" version="18.3.0">
|
||||
<artifact name="play-services-basement-18.3.0.aar">
|
||||
<sha256 value="6c11ae3eb2dd7f17373f919c4c557a70e4cf891bc0c9b66926a0a6445d654352" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-cloud-messaging" version="17.0.1">
|
||||
<artifact name="play-services-cloud-messaging-17.0.1.aar">
|
||||
<sha256 value="1e759adcf0350731ce4dc73b035705e4c7d08bdf7db069cc0468eca3e7bb9dc2" origin="Generated by Gradle"/>
|
||||
|
@ -4901,6 +4921,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="4c93d05e943d29852d2b3092b467ce8517a222363eb8584b706a47292bddd18a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-location" version="19.0.0">
|
||||
<artifact name="play-services-location-19.0.0.aar">
|
||||
<sha256 value="6b205c43ba5df751eca8ce9dae7a58effafac7d637fb4fc708a7522d1b99cf80" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-maps" version="18.0.2">
|
||||
<artifact name="play-services-maps-18.0.2.aar">
|
||||
<sha256 value="442a4687f44a266051853986d8e8f694ffc62e5fe752b613b356c66f9bf55d2d" origin="Generated by Gradle"/>
|
||||
|
@ -4911,6 +4936,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="44f50655578a21c579e1bb2dacaec4c545ec4df81393d6fd7236d664268056a9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-places-placereport" version="17.0.0">
|
||||
<artifact name="play-services-places-placereport-17.0.0.aar">
|
||||
<sha256 value="2c7fd63ad02f28150ae4ffe4615dac7d694d790e2c4667f777aedc8ee054e929" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-stats" version="17.0.2">
|
||||
<artifact name="play-services-stats-17.0.2.aar">
|
||||
<sha256 value="dd4314a53f49a378ec146103d36232b96c75454d29526336ccbdf132941764d3" origin="Generated by Gradle"/>
|
||||
|
@ -4926,6 +4956,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="03465cf53680da23d3d4a3995c0329b7f73b211d38791ecb36e591febb896664" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-tasks" version="18.1.0">
|
||||
<artifact name="play-services-tasks-18.1.0.aar">
|
||||
<sha256 value="d60575eae39350e6234858bc9d7d775375707ae82a684e6caf7f3e41a12e25a2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.android.gms" name="play-services-wallet" version="19.2.1">
|
||||
<artifact name="play-services-wallet-19.2.1.aar">
|
||||
<sha256 value="c1625a403df419d08e9da950fb72b04c9430b66bd702231941aa726b39942c1d" origin="Generated by Gradle"/>
|
||||
|
@ -8196,6 +8231,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="7b9f6960c6689d1a79d82ca3b00b8347bd57d15fe4070a3dd34e826e76136392" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core" version="1.6.0">
|
||||
<artifact name="kotlinx-coroutines-core-1.6.0.module">
|
||||
<sha256 value="512cfab68f00d7363461b5dd02637dd19048b364a89e1960f99784c705a27a7c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core" version="1.6.3">
|
||||
<artifact name="kotlinx-coroutines-core-1.6.3.module">
|
||||
<sha256 value="14039719f2100d91e0ab220f834f7e5f3578a81026137e4ec16a5c83ecd1740b" origin="Generated by Gradle"/>
|
||||
|
@ -8241,6 +8281,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="c885dd0281076c5843826de317e3cbcdc3d8859dbeef53ae1cfacd1b9c60f96e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.6.0">
|
||||
<artifact name="kotlinx-coroutines-core-jvm-1.6.0.module">
|
||||
<sha256 value="2cdc60217b955ce213fca1f14ee960b85dbd30f8a6966361237410d38a602ef1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.6.3">
|
||||
<artifact name="kotlinx-coroutines-core-jvm-1.6.3.jar">
|
||||
<sha256 value="58a497ab595d83bbbf28892a8b34ab57d94309a8742ee0eba43cb86408d235bf" origin="Generated by Gradle"/>
|
||||
|
|
|
@ -56,6 +56,7 @@ include(":benchmark")
|
|||
include(":microbenchmark")
|
||||
include(":video")
|
||||
include(":video-app")
|
||||
include(":billing")
|
||||
|
||||
project(":app").name = "Signal-Android"
|
||||
project(":paging").projectDir = file("paging/lib")
|
||||
|
|
Loading…
Add table
Reference in a new issue