Skip to content

Commit

Permalink
SLCOL-84: Add InAppPurchaseBloc (#192)
Browse files Browse the repository at this point in the history
* SLCOL-84: Add InAppPurchaseBloc

* SLCOL-84: Update IAP product IDs

* SLCOL-84: Complete IAP implementation

* SLCOL-84: Fix lint and android build

* SLCOL-84: Make InAppPurchaseConnection injectable

* SLCOL-84: Fix workflow

* SLCOL-84: Add translations
  • Loading branch information
campovski authored Apr 27, 2021
1 parent 41fbd8c commit 692d4e9
Show file tree
Hide file tree
Showing 27 changed files with 753 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ jobs:
- run: echo "${{ secrets.FIREBASE_CONFIG_FILE_ANDROID }}" | base64 --decode > android/app/google-services.json
- run: flutter pub get
- run: flutter packages pub run build_runner build
- run: flutter build appbundle
- run: flutter build appbundle --debug
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
- run: flutter pub get
- run: flutter packages pub run build_runner build
- run: sed -i'.bak' -e 's@package:http/src/client.dart@package:http/http.dart@g' lib/util/injection/injection.iconfig.dart
- run: sed -i'.bak' -e 's@package:in_app_purchase/src/in_app_purchase/in_app_purchase_connection.dart@package:in_app_purchase/in_app_purchase.dart@g' lib/util/injection/injection.iconfig.dart
- run: flutter analyze
- run: flutter test --coverage .
- uses: codecov/[email protected]
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ test/coverage_test.dart
# Firebase configuration files
ios/Runner/GoogleService-Info.plist
android/app/google-services.json

# Release config
/android/key.properties
25 changes: 21 additions & 4 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ apply plugin: 'com.google.gms.google-services'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
compileSdkVersion 28
compileSdkVersion 29

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
Expand All @@ -40,17 +46,27 @@ android {
defaultConfig {
applicationId "com.stelynx.collectio"
minSdkVersion 22
targetSdkVersion 28
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}

signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
}
}
Expand All @@ -64,6 +80,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.firebase:firebase-analytics:17.2.2'
implementation 'com.android.support:multidex:1.0.3'
implementation "com.android.billingclient:billing-ktx:3.0.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
Expand Down
9 changes: 8 additions & 1 deletion assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"collections": "Sammlungen",
"loadCollections": "Abrufen von Sammlungen ...",
"loadProfile": "Profildaten abrufen ...",
"buyPremiumCollection": "Kaufen Premium-Sammlungen",
"logout": "Abmelden",
"camera": "Kamera",
"photoLibrary": "Fotobibliothek",
Expand Down Expand Up @@ -83,5 +84,11 @@
"collectionDeletionFailed": "Sammlung konnte nicht gelöscht werden!",
"collectionItemDeletionFailed": "Artikel konnte nicht gelöscht werden!",

"autocompleteNoText": "Tippe ein ..."
"autocompleteNoText": "Tippe ein ...",

"inAppPurchaseNotAvailable": "In-App-Käufe sind derzeit nicht verfügbar",
"inAppPurchaseNotAvailableContent": "In-App-Käufe sind derzeit nicht verfügbar. Bitte versuchen Sie es später erneut.",
"availableInAppPurchases": "Verfügbare Premium Collection Packs",
"inAppPurchaseSuccessful": "Ihr Kauf war erfolgreich!",
"inAppPurchaseError": "Ihr Kauf wurde nicht abgeschlossen!"
}
9 changes: 8 additions & 1 deletion assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"collections": "Collections",
"loadingCollections": "Retrieving collections ...",
"loadingProfile": "Retrieving profile data ...",
"buyPremiumCollection": "Buy premium collections",
"logout": "Logout",
"camera": "Camera",
"photoLibrary": "Photo library",
Expand Down Expand Up @@ -83,5 +84,11 @@
"collectionDeletionFailed": "Collection could not be deleted!",
"collectionItemDeletionFailed": "Item could not be deleted!",

"autocompleteNoText": "Start typing ..."
"autocompleteNoText": "Start typing ...",

"inAppPurchaseNotAvailable": "In-App Purchases are currently not available",
"inAppPurchaseNotAvailableContent": "In-App Purchases are currently not available. Please, try again later.",
"availableInAppPurchases": "Available Premium Collection Packs",
"inAppPurchaseSuccessful": "Your purchase was successful!",
"inAppPurchaseError": "Your purchase was not completed!"
}
9 changes: 8 additions & 1 deletion assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"collections": "Colecciones",
"loadingCollections": "Recuperando colecciones ...",
"loadingProfile": "Recuperando datos de perfil ...",
"buyPremiumCollection": "Comprar colecciones premium",
"logout": "Cerrar sesión",
"camera": "Cámara",
"photoLibrary": "Biblioteca de fotos",
Expand Down Expand Up @@ -83,5 +84,11 @@
"collectionDeletionFailed": "¡La colección no se pudo eliminar!",
"collectionItemDeletionFailed": "¡El elemento no se pudo eliminar!",

"autocompleteNoText": "Comience a escribir ..."
"autocompleteNoText": "Comience a escribir ...",

"inAppPurchaseNotAvailable": "Las compras integradas en la aplicación no están disponibles actualmente",
"inAppPurchaseNotAvailableContent": "Las compras integradas en la aplicación no están disponibles actualmente. Inténtelo de nuevo más tarde",
"availableInAppPurchases": "Paquetes de colección premium disponibles",
"inAppPurchaseSuccessful": "¡Tu compra se realizó correctamente!",
"inAppPurchaseError": "¡Su compra no se completó!"
}
9 changes: 8 additions & 1 deletion assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"collections": "Collections",
"loadingCollections": "Récupération des collections ...",
"loadingProfile": "Récupération des données de profil ...",
"buyPremiumCollection": "Acheter des collections premium",
"logout": "Déconnexion",
"camera": "Camera",
"photoLibrary": "Photothèque",
Expand Down Expand Up @@ -83,5 +84,11 @@
"collectionDeletionFailed": "La collection n'a pas pu être supprimée!",
"collectionItemDeletionFailed": "L'élément n'a pas pu être supprimé!",

"autocompleteNoText": "Commencez à taper ..."
"autocompleteNoText": "Commencez à taper ...",

"inAppPurchaseNotAvailable": "Les achats intégrés ne sont actuellement pas disponibles",
"inAppPurchaseNotAvailableContent": "Les achats intégrés ne sont actuellement pas disponibles. Veuillez réessayer plus tard.",
"availableInAppPurchases": "Packs de collection Premium disponibles",
"inAppPurchaseSuccessful": "Votre achat a réussi!",
"inAppPurchaseError": "Votre achat n'est pas terminé!"
}
9 changes: 8 additions & 1 deletion assets/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"collections": "Collezioni",
"loadingCollections": "Recupero raccolte ...",
"loadingProfile": "Recupero dati profilo ...",
"buyPremiumCollection": "Acquista collezioni premium",
"logout": "Disconnettersi",
"camera": "Camera",
"photoLibrary": "Fototeca",
Expand Down Expand Up @@ -83,5 +84,11 @@
"collectionDeletionFailed": "Impossibile eliminare la raccolta!",
"collectionItemDeletionFailed": "L'articolo non può essere cancellato!",

"autocompleteNoText": "Inizia a digitare ..."
"autocompleteNoText": "Inizia a digitare ...",

"inAppPurchaseNotAvailable": "Gli acquisti in-app non sono attualmente disponibili",
"inAppPurchaseNotAvailableContent": "Gli acquisti in-app non sono attualmente disponibili. Riprova più tardi.",
"availableInAppPurchases": "Pacchetti collezione Premium disponibili",
"inAppPurchaseSuccessful": "Il tuo acquisto è andato a buon fine!",
"inAppPurchaseError": "Il tuo acquisto non è stato completato!"
}
9 changes: 8 additions & 1 deletion assets/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"collections": "Коллекции",
"loadingCollections": "Получение коллекций ...",
"loadingProfile": "Получение данных профиля ...",
"buyPremiumCollection": "Купить премиальные коллекции",
"logout": "Выйти",
"camera": "Камера",
"photoLibrary": "Библиотека фотографий",
Expand Down Expand Up @@ -83,5 +84,11 @@
"collectionDeletionFailed": "Невозможно удалить коллекцию!",
"collectionItemDeletionFailed": "Элемент не может быть удален!",

"autocompleteNoText": "Начните печатать ..."
"autocompleteNoText": "Начните печатать ...",

"inAppPurchaseNotAvailable": "Покупки в приложении в настоящее время недоступны",
"inAppPurchaseNotAvailableContent": "Покупки в приложении в настоящее время недоступны. Повторите попытку позже.",
"availableInAppPurchases": "Доступные пакеты Premium Collection",
"inAppPurchaseSuccessful": "Ваша покупка прошла успешно!",
"inAppPurchaseError": "Ваша покупка не была совершена!"
}
9 changes: 8 additions & 1 deletion assets/i18n/sl.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"collections": "Zbirke",
"loadingCollections": "Pridobivamo zbirke ...",
"loadingProfile": "Pridobivamo profil ...",
"buyPremiumCollection": "Nakup premium zbirk",
"logout": "Odjavi se",
"camera": "Kamera",
"photoLibrary": "Fotografije",
Expand Down Expand Up @@ -83,5 +84,11 @@
"collectionDeletionFailed": "Zbirke ni bilo mogoče izbrisati",
"collectionItemDeletionFailed": "Vnosa ni bilo mogoče izbrisati",

"autocompleteNoText": "Začnite tipkati ..."
"autocompleteNoText": "Začnite tipkati ...",

"inAppPurchaseNotAvailable": "Nakupi znotraj aplikacije trenutno niso na voljo",
"inAppPurchaseNotAvailableContent": "Nakupi znotraj aplikacije trenutno niso na voljo. Prosimo, poskusite kasneje.",
"availableInAppPurchases": "Paketi premium zbirk na voljo",
"inAppPurchaseSuccessful": "Vaš nakup je bil uspešen!",
"inAppPurchaseError": "Vaš nakup ni bil zaključen!"
}
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@ PODS:
- TOCropViewController (~> 2.5.2)
- image_picker (0.0.1):
- Flutter
- in_app_purchase (0.0.1):
- Flutter
- leveldb-library (1.22.1)
- nanopb (1.30906.0):
- nanopb/decode (= 1.30906.0)
Expand All @@ -392,6 +394,7 @@ DEPENDENCIES:
- google_maps_flutter (from `.symlinks/plugins/google_maps_flutter/ios`)
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
- image_picker (from `.symlinks/plugins/image_picker/ios`)
- in_app_purchase (from `.symlinks/plugins/in_app_purchase/ios`)

SPEC REPOS:
trunk:
Expand Down Expand Up @@ -444,6 +447,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/image_cropper/ios"
image_picker:
:path: ".symlinks/plugins/image_picker/ios"
in_app_purchase:
:path: ".symlinks/plugins/in_app_purchase/ios"

SPEC CHECKSUMS:
abseil: 6c8eb7892aefa08d929b39f9bb108e5367e3228f
Expand Down Expand Up @@ -476,6 +481,7 @@ SPEC CHECKSUMS:
GTMSessionFetcher: 6f5c8abbab8a9bce4bb3f057e317728ec6182b10
image_cropper: 3c16d7651730ffe85897f5a1c4e2547e6b54989a
image_picker: 66aa71bc96850a90590a35d4c4a2907b0d823109
in_app_purchase: 3e2155afa9d03d4fa32d9e62d567885080ce97d6
leveldb-library: 50c7b45cbd7bf543c81a468fe557a16ae3db8729
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151
Expand Down
24 changes: 22 additions & 2 deletions lib/app/bloc/collections/new_collection_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import '../../../util/error/data_failure.dart';
import '../../../util/function/id_generator.dart';
import '../../../util/function/image_name_generator.dart';
import '../../../util/function/listable_finder.dart';
import '../in_app_purchase/in_app_purchase_bloc.dart';
import '../profile/profile_bloc.dart';
import 'collections_bloc.dart';

Expand All @@ -34,14 +35,30 @@ class NewCollectionBloc extends Bloc<NewCollectionEvent, NewCollectionState> {
final CollectionsFacade _collectionsFacade;
final CollectionsBloc _collectionsBloc;
final ProfileBloc _profileBloc;
final InAppPurchaseBloc _iapBloc;
StreamSubscription _iapBlocStreamSubscription;

NewCollectionBloc({
@required CollectionsFacade collectionsFacade,
@required ProfileBloc profileBloc,
@required CollectionsBloc collectionsBloc,
@required InAppPurchaseBloc inAppPurchaseBloc,
}) : _collectionsFacade = collectionsFacade,
_profileBloc = profileBloc,
_collectionsBloc = collectionsBloc;
_collectionsBloc = collectionsBloc,
_iapBloc = inAppPurchaseBloc {
_iapBlocStreamSubscription = _iapBloc.listen((InAppPurchaseState state) {
if (state.purchaseState == InAppPurchasePurchaseState.success) {
this.add(IsPremiumChangedNewCollectionEvent());
}
});
}

@override
Future<void> close() {
_iapBlocStreamSubscription.cancel();
return super.close();
}

@override
NewCollectionState get initialState => InitialNewCollectionState();
Expand Down Expand Up @@ -75,7 +92,7 @@ class NewCollectionBloc extends Bloc<NewCollectionEvent, NewCollectionState> {
overrideDataFailure: true,
);
} else if (event is IsPremiumChangedNewCollectionEvent) {
if (!_profileBloc.canCreatePremiumCollection()) return;
if (!canCreatePremiumCollection()) return;

yield state.copyWith(
isPremium: !state.isPremium,
Expand Down Expand Up @@ -193,6 +210,9 @@ class NewCollectionBloc extends Bloc<NewCollectionEvent, NewCollectionState> {
}
}
}

bool canCreatePremiumCollection() =>
_profileBloc.canCreatePremiumCollection();
}

@test
Expand Down
Loading

0 comments on commit 692d4e9

Please sign in to comment.