diff --git a/in_app_purchases/.gitignore b/in_app_purchases/.gitignore deleted file mode 100644 index 9f11b755a1..0000000000 --- a/in_app_purchases/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.idea/ diff --git a/in_app_purchases/codelab_rebuild.yaml b/in_app_purchases/codelab_rebuild.yaml index e75fdc80b1..befeb71d4f 100644 --- a/in_app_purchases/codelab_rebuild.yaml +++ b/in_app_purchases/codelab_rebuild.yaml @@ -14,7 +14,7 @@ steps: patch-u: | --- b/in_app_purchases/step_00/app/.gitignore +++ a/in_app_purchases/step_00/app/.gitignore - @@ -43,3 +43,9 @@ app.*.map.json + @@ -43,3 +43,8 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release @@ -23,10 +23,9 @@ steps: +GoogleService-Info.plist +google-services.json +ios/firebase_app_id_file.json - +lib/firebase_options.dart - name: Add app dependencies path: steps/app - flutter: pub add cloud_firestore cupertino_icons firebase_auth firebase_core google_sign_in http in_app_purchase in_app_purchase_platform_interface intl provider + flutter: pub add cloud_firestore cupertino_icons firebase_auth firebase_core google_sign_in http intl provider - name: Patch app/pubspec.yaml path: steps/app/pubspec.yaml patch-u: | @@ -39,7 +38,7 @@ steps: publish_to: 'none' version: 0.1.0 - @@ -27,3 +27,6 @@ dev_dependencies: + @@ -25,3 +25,6 @@ dev_dependencies: flutter: uses-material-design: true @@ -16734,7 +16733,6 @@ steps: assets/*.p8 assets/appstore.token assets/service-account*.json - lib/constants.dart - name: Replace analysis_options.yaml path: steps/dart-backend/analysis_options.yaml replace-contents: | @@ -17179,6 +17177,18 @@ steps: from: steps to: step_00 + - name: step_03 + steps: + - name: Remove step_03 + rmdir: step_03 + - name: Add dependencies + path: steps/app + flutter: pub add in_app_purchase dev:in_app_purchase_platform_interface + - name: Copy to step_03 + copydir: + from: steps + to: step_03 + - name: step_07 steps: - name: Remove step_07 @@ -18550,91 +18560,9 @@ steps: - name: Patch app/lib/logic/dash_purchases.dart path: steps/app/lib/logic/dash_purchases.dart patch-u: | - --- b/in_app_purchases/complete/app/lib/logic/dash_purchases.dart - +++ a/in_app_purchases/complete/app/lib/logic/dash_purchases.dart - @@ -1,3 +1,5 @@ - +// ignore_for_file: avoid_print - + - import 'dart:async'; - import 'dart:convert'; - - @@ -17,10 +19,10 @@ import 'firebase_notifier.dart'; - class DashPurchases extends ChangeNotifier { - DashCounter counter; - FirebaseNotifier firebaseNotifier; - + IAPRepo iapRepo; - StoreState storeState = StoreState.loading; - late StreamSubscription> _subscription; - List products = []; - - IAPRepo iapRepo; - - bool get beautifiedDash => _beautifiedDashUpgrade; - bool _beautifiedDashUpgrade = false; - @@ -44,6 +46,7 @@ class DashPurchases extends ChangeNotifier { - notifyListeners(); - return; - } - + - const ids = { - storeKeyConsumable, - storeKeySubscription, - @@ -58,8 +61,8 @@ class DashPurchases extends ChangeNotifier { - - @override - void dispose() { - - _subscription.cancel(); - iapRepo.removeListener(purchasesUpdate); - + _subscription.cancel(); - super.dispose(); - } - - @@ -126,8 +129,10 @@ class DashPurchases extends ChangeNotifier { - headers: headers, - ); - if (response.statusCode == 200) { - + print('Successfully verified purchase'); - return true; - } else { - + print('failed request: ${response.statusCode} - ${response.body}'); - return false; - } - } - @@ -137,7 +142,7 @@ class DashPurchases extends ChangeNotifier { - } - - void _updateStreamOnError(dynamic error) { - - //Handle error here - + print(error); - } - - void purchasesUpdate() { - @@ -158,21 +163,21 @@ class DashPurchases extends ChangeNotifier { - // purchases page. - if (iapRepo.hasActiveSubscription) { - counter.applyPaidMultiplier(); - - for (var element in subscriptions) { - + for (final element in subscriptions) { - _updateStatus(element, ProductStatus.purchased); - } - } else { - counter.removePaidMultiplier(); - - for (var element in subscriptions) { - + for (final element in subscriptions) { - _updateStatus(element, ProductStatus.purchasable); - } - } - - - // Set the Dash beautifier and show/hide purchased on - + // Set the dash beautifier and show/hide purchased on - // the purchases page. - if (iapRepo.hasUpgrade != _beautifiedDashUpgrade) { - _beautifiedDashUpgrade = iapRepo.hasUpgrade; - - for (var element in upgrades) { - + for (final element in upgrades) { - _updateStatus( - element, - _beautifiedDashUpgrade - @@ -184,8 +189,8 @@ class DashPurchases extends ChangeNotifier { + --- b/in_app_purchases/step_11/app/lib/logic/dash_purchases.dart + +++ a/in_app_purchases/step_11/app/lib/logic/dash_purchases.dart + @@ -184,8 +184,8 @@ class DashPurchases extends ChangeNotifier { } void _updateStatus(PurchasableProduct product, ProductStatus status) { diff --git a/in_app_purchases/complete/app/.gitignore b/in_app_purchases/complete/app/.gitignore index 6f1f0459d5..9b7a64b527 100644 --- a/in_app_purchases/complete/app/.gitignore +++ b/in_app_purchases/complete/app/.gitignore @@ -48,4 +48,3 @@ app.*.map.json GoogleService-Info.plist google-services.json ios/firebase_app_id_file.json -lib/firebase_options.dart diff --git a/in_app_purchases/complete/app/ios/Runner.xcodeproj/project.pbxproj b/in_app_purchases/complete/app/ios/Runner.xcodeproj/project.pbxproj index e437d48aa4..6f2fb5b153 100644 --- a/in_app_purchases/complete/app/ios/Runner.xcodeproj/project.pbxproj +++ b/in_app_purchases/complete/app/ios/Runner.xcodeproj/project.pbxproj @@ -362,7 +362,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -542,7 +541,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -565,7 +563,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/in_app_purchases/complete/app/lib/logic/dash_purchases.dart b/in_app_purchases/complete/app/lib/logic/dash_purchases.dart index a8c7e0a12f..c73eaa4c56 100644 --- a/in_app_purchases/complete/app/lib/logic/dash_purchases.dart +++ b/in_app_purchases/complete/app/lib/logic/dash_purchases.dart @@ -1,5 +1,3 @@ -// ignore_for_file: avoid_print - import 'dart:async'; import 'dart:convert'; @@ -19,10 +17,10 @@ import 'firebase_notifier.dart'; class DashPurchases extends ChangeNotifier { DashCounter counter; FirebaseNotifier firebaseNotifier; - IAPRepo iapRepo; StoreState storeState = StoreState.loading; late StreamSubscription> _subscription; List products = []; + IAPRepo iapRepo; bool get beautifiedDash => _beautifiedDashUpgrade; bool _beautifiedDashUpgrade = false; @@ -46,7 +44,6 @@ class DashPurchases extends ChangeNotifier { notifyListeners(); return; } - const ids = { storeKeyConsumable, storeKeySubscription, @@ -61,8 +58,8 @@ class DashPurchases extends ChangeNotifier { @override void dispose() { - iapRepo.removeListener(purchasesUpdate); _subscription.cancel(); + iapRepo.removeListener(purchasesUpdate); super.dispose(); } @@ -129,10 +126,8 @@ class DashPurchases extends ChangeNotifier { headers: headers, ); if (response.statusCode == 200) { - print('Successfully verified purchase'); return true; } else { - print('failed request: ${response.statusCode} - ${response.body}'); return false; } } @@ -142,7 +137,7 @@ class DashPurchases extends ChangeNotifier { } void _updateStreamOnError(dynamic error) { - print(error); + //Handle error here } void purchasesUpdate() { @@ -163,21 +158,21 @@ class DashPurchases extends ChangeNotifier { // purchases page. if (iapRepo.hasActiveSubscription) { counter.applyPaidMultiplier(); - for (final element in subscriptions) { + for (var element in subscriptions) { _updateStatus(element, ProductStatus.purchased); } } else { counter.removePaidMultiplier(); - for (final element in subscriptions) { + for (var element in subscriptions) { _updateStatus(element, ProductStatus.purchasable); } } - // Set the dash beautifier and show/hide purchased on + // Set the Dash beautifier and show/hide purchased on // the purchases page. if (iapRepo.hasUpgrade != _beautifiedDashUpgrade) { _beautifiedDashUpgrade = iapRepo.hasUpgrade; - for (final element in upgrades) { + for (var element in upgrades) { _updateStatus( element, _beautifiedDashUpgrade diff --git a/in_app_purchases/complete/app/pubspec.yaml b/in_app_purchases/complete/app/pubspec.yaml index 682968a175..b2920aa5c5 100644 --- a/in_app_purchases/complete/app/pubspec.yaml +++ b/in_app_purchases/complete/app/pubspec.yaml @@ -4,26 +4,26 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: flutter: sdk: flutter - cloud_firestore: ^5.5.1 + cloud_firestore: ^5.6.2 cupertino_icons: ^1.0.8 - firebase_auth: ^5.3.4 - firebase_core: ^3.8.1 + firebase_auth: ^5.4.1 + firebase_core: ^3.10.1 google_sign_in: ^6.2.2 - http: ^1.2.2 - in_app_purchase: ^3.2.0 - in_app_purchase_platform_interface: ^1.4.0 + http: ^1.3.0 intl: ^0.20.1 provider: ^6.1.2 + in_app_purchase: ^3.2.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^5.0.0 + in_app_purchase_platform_interface: ^1.4.0 flutter: uses-material-design: true diff --git a/in_app_purchases/complete/dart-backend/.gitignore b/in_app_purchases/complete/dart-backend/.gitignore index 49d9e600e4..f7f0055f83 100644 --- a/in_app_purchases/complete/dart-backend/.gitignore +++ b/in_app_purchases/complete/dart-backend/.gitignore @@ -9,4 +9,3 @@ build/ assets/*.p8 assets/appstore.token assets/service-account*.json -lib/constants.dart diff --git a/in_app_purchases/complete/dart-backend/pubspec.yaml b/in_app_purchases/complete/dart-backend/pubspec.yaml index 7a168d9aee..da8365432a 100644 --- a/in_app_purchases/complete/dart-backend/pubspec.yaml +++ b/in_app_purchases/complete/dart-backend/pubspec.yaml @@ -4,13 +4,13 @@ version: 1.0.0 # repository: https://github.com/my_org/my_repo environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: app_store_server_sdk: ^1.2.9 googleapis: ^13.2.0 googleapis_auth: ^1.6.0 - http: ^1.2.2 + http: ^1.3.0 shelf: ^1.4.0 shelf_router: ^1.1.0 diff --git a/in_app_purchases/step_00/app/.gitignore b/in_app_purchases/step_00/app/.gitignore index 6f1f0459d5..9b7a64b527 100644 --- a/in_app_purchases/step_00/app/.gitignore +++ b/in_app_purchases/step_00/app/.gitignore @@ -48,4 +48,3 @@ app.*.map.json GoogleService-Info.plist google-services.json ios/firebase_app_id_file.json -lib/firebase_options.dart diff --git a/in_app_purchases/step_00/app/ios/Runner.xcodeproj/project.pbxproj b/in_app_purchases/step_00/app/ios/Runner.xcodeproj/project.pbxproj index e437d48aa4..6f2fb5b153 100644 --- a/in_app_purchases/step_00/app/ios/Runner.xcodeproj/project.pbxproj +++ b/in_app_purchases/step_00/app/ios/Runner.xcodeproj/project.pbxproj @@ -362,7 +362,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -542,7 +541,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -565,7 +563,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/in_app_purchases/step_00/app/pubspec.yaml b/in_app_purchases/step_00/app/pubspec.yaml index 682968a175..e77617277b 100644 --- a/in_app_purchases/step_00/app/pubspec.yaml +++ b/in_app_purchases/step_00/app/pubspec.yaml @@ -4,19 +4,17 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: flutter: sdk: flutter - cloud_firestore: ^5.5.1 + cloud_firestore: ^5.6.2 cupertino_icons: ^1.0.8 - firebase_auth: ^5.3.4 - firebase_core: ^3.8.1 + firebase_auth: ^5.4.1 + firebase_core: ^3.10.1 google_sign_in: ^6.2.2 - http: ^1.2.2 - in_app_purchase: ^3.2.0 - in_app_purchase_platform_interface: ^1.4.0 + http: ^1.3.0 intl: ^0.20.1 provider: ^6.1.2 diff --git a/in_app_purchases/step_00/dart-backend/.gitignore b/in_app_purchases/step_00/dart-backend/.gitignore index 49d9e600e4..f7f0055f83 100644 --- a/in_app_purchases/step_00/dart-backend/.gitignore +++ b/in_app_purchases/step_00/dart-backend/.gitignore @@ -9,4 +9,3 @@ build/ assets/*.p8 assets/appstore.token assets/service-account*.json -lib/constants.dart diff --git a/in_app_purchases/step_00/dart-backend/pubspec.yaml b/in_app_purchases/step_00/dart-backend/pubspec.yaml index 7a168d9aee..da8365432a 100644 --- a/in_app_purchases/step_00/dart-backend/pubspec.yaml +++ b/in_app_purchases/step_00/dart-backend/pubspec.yaml @@ -4,13 +4,13 @@ version: 1.0.0 # repository: https://github.com/my_org/my_repo environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: app_store_server_sdk: ^1.2.9 googleapis: ^13.2.0 googleapis_auth: ^1.6.0 - http: ^1.2.2 + http: ^1.3.0 shelf: ^1.4.0 shelf_router: ^1.1.0 diff --git a/in_app_purchases/step_03/app/.gitignore b/in_app_purchases/step_03/app/.gitignore new file mode 100644 index 0000000000..9b7a64b527 --- /dev/null +++ b/in_app_purchases/step_03/app/.gitignore @@ -0,0 +1,50 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +# Configuration files +GoogleService-Info.plist +google-services.json +ios/firebase_app_id_file.json diff --git a/in_app_purchases/step_03/app/analysis_options.yaml b/in_app_purchases/step_03/app/analysis_options.yaml new file mode 100644 index 0000000000..e2badd73ea --- /dev/null +++ b/in_app_purchases/step_03/app/analysis_options.yaml @@ -0,0 +1 @@ +include: ../../../analysis_options.yaml diff --git a/in_app_purchases/step_03/app/android/.gitignore b/in_app_purchases/step_03/app/android/.gitignore new file mode 100644 index 0000000000..55afd919c6 --- /dev/null +++ b/in_app_purchases/step_03/app/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/in_app_purchases/step_03/app/android/app/build.gradle b/in_app_purchases/step_03/app/android/app/build.gradle new file mode 100644 index 0000000000..149d49474c --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/build.gradle @@ -0,0 +1,43 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +android { + namespace = "com.example.dashclicker" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.dashclicker" + // Per https://firebase.google.com/support/release-notes/android firebase_auth requires minSdk 23 + minSdk = 23 + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + 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.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/in_app_purchases/step_03/app/android/app/src/debug/AndroidManifest.xml b/in_app_purchases/step_03/app/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000000..399f6981d5 --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/in_app_purchases/step_03/app/android/app/src/main/AndroidManifest.xml b/in_app_purchases/step_03/app/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..3b66d68d89 --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/in_app_purchases/step_03/app/android/app/src/main/kotlin/com/example/dashclicker/MainActivity.kt b/in_app_purchases/step_03/app/android/app/src/main/kotlin/com/example/dashclicker/MainActivity.kt new file mode 100644 index 0000000000..2879c0343b --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/main/kotlin/com/example/dashclicker/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.dashclicker + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/drawable-v21/launch_background.xml b/in_app_purchases/step_03/app/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000000..f74085f3f6 --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/drawable/launch_background.xml b/in_app_purchases/step_03/app/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000000..304732f884 --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..db77bb4b7b Binary files /dev/null and b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..17987b79bb Binary files /dev/null and b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..09d4391482 Binary files /dev/null and b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d5f1c8d34e Binary files /dev/null and b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4d6372eebd Binary files /dev/null and b/in_app_purchases/step_03/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/values-night/styles.xml b/in_app_purchases/step_03/app/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000000..06952be745 --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/in_app_purchases/step_03/app/android/app/src/main/res/values/styles.xml b/in_app_purchases/step_03/app/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..cb1ef88056 --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/in_app_purchases/step_03/app/android/app/src/profile/AndroidManifest.xml b/in_app_purchases/step_03/app/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000000..399f6981d5 --- /dev/null +++ b/in_app_purchases/step_03/app/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/in_app_purchases/step_03/app/android/build.gradle b/in_app_purchases/step_03/app/android/build.gradle new file mode 100644 index 0000000000..d2ffbffa4c --- /dev/null +++ b/in_app_purchases/step_03/app/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/in_app_purchases/step_03/app/android/gradle.properties b/in_app_purchases/step_03/app/android/gradle.properties new file mode 100644 index 0000000000..2597170821 --- /dev/null +++ b/in_app_purchases/step_03/app/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/in_app_purchases/step_03/app/android/gradle/wrapper/gradle-wrapper.properties b/in_app_purchases/step_03/app/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..7bb2df6ba6 --- /dev/null +++ b/in_app_purchases/step_03/app/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip diff --git a/in_app_purchases/step_03/app/android/settings.gradle b/in_app_purchases/step_03/app/android/settings.gradle new file mode 100644 index 0000000000..a42444ded0 --- /dev/null +++ b/in_app_purchases/step_03/app/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.2.1" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false +} + +include ":app" diff --git a/in_app_purchases/step_03/app/assets/dash.png b/in_app_purchases/step_03/app/assets/dash.png new file mode 100644 index 0000000000..36e499fe7d Binary files /dev/null and b/in_app_purchases/step_03/app/assets/dash.png differ diff --git a/in_app_purchases/step_03/app/assets/dash_old.png b/in_app_purchases/step_03/app/assets/dash_old.png new file mode 100644 index 0000000000..84d2120631 Binary files /dev/null and b/in_app_purchases/step_03/app/assets/dash_old.png differ diff --git a/in_app_purchases/step_03/app/ios/.gitignore b/in_app_purchases/step_03/app/ios/.gitignore new file mode 100644 index 0000000000..7a7f9873ad --- /dev/null +++ b/in_app_purchases/step_03/app/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/in_app_purchases/step_03/app/ios/Flutter/AppFrameworkInfo.plist b/in_app_purchases/step_03/app/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000000..7c56964006 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/in_app_purchases/step_03/app/ios/Flutter/Debug.xcconfig b/in_app_purchases/step_03/app/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000000..ec97fc6f30 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/in_app_purchases/step_03/app/ios/Flutter/Release.xcconfig b/in_app_purchases/step_03/app/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000000..c4855bfe20 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/in_app_purchases/step_03/app/ios/Podfile b/in_app_purchases/step_03/app/ios/Podfile new file mode 100644 index 0000000000..0a27aa76d9 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Podfile @@ -0,0 +1,44 @@ +# Per https://firebase.google.com/support/release-notes/ios cloud_firestore requires a minimum iOS version of 13.0 +platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.pbxproj b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..6f2fb5b153 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,616 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.dashclicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.dashclicker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.dashclicker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.dashclicker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.dashclicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.dashclicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..919434a625 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/in_app_purchases/step_03/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000000..8e3ca5dfe1 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/in_app_purchases/step_03/app/ios/Runner.xcworkspace/contents.xcworkspacedata b/in_app_purchases/step_03/app/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..1d526a16ed --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/in_app_purchases/step_03/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/in_app_purchases/step_03/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/in_app_purchases/step_03/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/in_app_purchases/step_03/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/in_app_purchases/step_03/app/ios/Runner/AppDelegate.swift b/in_app_purchases/step_03/app/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000000..626664468b --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d36b1fab2d --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000..dc9ada4725 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000..7353c41ecf Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000..797d452e45 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000..6ed2d933e1 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000..4cd7b0099c Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000..fe730945a0 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000000..321773cd85 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000000..797d452e45 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000..502f463a9b Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000..0ec3034392 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000..0ec3034392 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000..e9f5fea27c Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000..84ac32ae7d Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000000..8953cba090 Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000..0467bf12aa Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000000..0bedcf2fd4 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000000..89c2725b70 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/in_app_purchases/step_03/app/ios/Runner/Base.lproj/LaunchScreen.storyboard b/in_app_purchases/step_03/app/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..f2e259c7c9 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/in_app_purchases/step_03/app/ios/Runner/Base.lproj/Main.storyboard b/in_app_purchases/step_03/app/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..f3c28516fb --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/in_app_purchases/step_03/app/ios/Runner/Info.plist b/in_app_purchases/step_03/app/ios/Runner/Info.plist new file mode 100644 index 0000000000..25150f615c --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Dashclicker + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + dashclicker + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + GIDClientID + 556020018095-tllu36cg2t30mn5baqa5c44qc7lf993l.apps.googleusercontent.com + CFBundleURLTypes + + + CFBundleURLSchemes + + com.googleusercontent.apps.556020018095-2ndn8hid07kl1m7d4o0q1i02kdkv0150 + + + + + diff --git a/in_app_purchases/step_03/app/ios/Runner/Runner-Bridging-Header.h b/in_app_purchases/step_03/app/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000000..308a2a560b --- /dev/null +++ b/in_app_purchases/step_03/app/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/in_app_purchases/step_03/app/ios/RunnerTests/RunnerTests.swift b/in_app_purchases/step_03/app/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000..86a7c3b1b6 --- /dev/null +++ b/in_app_purchases/step_03/app/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/in_app_purchases/step_03/app/lib/constants.dart b/in_app_purchases/step_03/app/lib/constants.dart new file mode 100644 index 0000000000..2d6c072858 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/constants.dart @@ -0,0 +1,7 @@ +const cloudRegion = 'europe-west1'; +const storeKeyConsumable = 'dash_consumable_2k'; +const storeKeyUpgrade = 'dash_upgrade_3d'; +const storeKeySubscription = 'dash_subscription_doubler'; + +// TODO: Replace by your own server IP +const serverIp = '192.168.178.46'; diff --git a/in_app_purchases/step_03/app/lib/firebase_options.dart b/in_app_purchases/step_03/app/lib/firebase_options.dart new file mode 100644 index 0000000000..feaa15d1c9 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/firebase_options.dart @@ -0,0 +1,69 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for web - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for macos - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'API KEY', + appId: 'APP ID', + messagingSenderId: 'SENDER ID', + projectId: 'PROJECT ID', + storageBucket: 'STORAGE BUCKET', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'API KEY', + appId: 'APP ID', + messagingSenderId: 'SENDER ID', + projectId: 'PROJECT ID', + storageBucket: 'STORAGE BUCKET', + iosClientId: 'CLIENT ID', + iosBundleId: 'BUNDLE ID', + ); +} diff --git a/in_app_purchases/step_03/app/lib/logic/dash_counter.dart b/in_app_purchases/step_03/app/lib/logic/dash_counter.dart new file mode 100644 index 0000000000..a6c90415bd --- /dev/null +++ b/in_app_purchases/step_03/app/lib/logic/dash_counter.dart @@ -0,0 +1,71 @@ +import 'dart:async'; + +import 'package:flutter/widgets.dart'; +import 'package:intl/intl.dart'; + +const updatesPerSecond = 10; + +class DashCounter extends ChangeNotifier { + final numberFormatter = NumberFormat.compactLong(); + + int get count => _count.floor(); + + String get countString => _countString; + String _countString = '0'; + double _count = 0; + late Timer timer; + + double _autoIncrementCount = 0; + int _multiplier = 1; + + DashCounter() { + timer = Timer.periodic( + const Duration(milliseconds: 1000 ~/ updatesPerSecond), + (timer) => _updateWithAutoIncrement(), + ); + } + + @override + void dispose() { + timer.cancel(); + super.dispose(); + } + + void increment() { + _increment(1); + } + + void addAutoIncrement({ + required double incrementPerSecond, + required int costs, + }) { + _count -= costs; + _autoIncrementCount += incrementPerSecond; + notifyListeners(); + } + + void _updateWithAutoIncrement() { + _increment(_autoIncrementCount * _multiplier / updatesPerSecond); + } + + void _increment(double increment) { + var oldCount = _countString; + _count += increment; + _countString = numberFormatter.format(count); + if (_countString != oldCount) { + notifyListeners(); + } + } + + void applyPaidMultiplier() { + _multiplier = 10; + } + + void removePaidMultiplier() { + _multiplier = 1; + } + + void addBoughtDashes(int amount) { + _increment(amount.toDouble()); + } +} diff --git a/in_app_purchases/step_03/app/lib/logic/dash_purchases.dart b/in_app_purchases/step_03/app/lib/logic/dash_purchases.dart new file mode 100644 index 0000000000..e3e0c304ac --- /dev/null +++ b/in_app_purchases/step_03/app/lib/logic/dash_purchases.dart @@ -0,0 +1,40 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import '../model/purchasable_product.dart'; +import '../model/store_state.dart'; +import 'dash_counter.dart'; + +class DashPurchases extends ChangeNotifier { + DashCounter counter; + StoreState storeState = StoreState.available; + List products = [ + PurchasableProduct( + 'Spring is in the air', + 'Many dashes flying out from their nests', + '\$0.99', + ), + PurchasableProduct( + 'Jet engine', + 'Doubles you clicks per second for a day', + '\$1.99', + ), + ]; + + bool get beautifiedDash => false; + + DashPurchases(this.counter); + + Future buy(PurchasableProduct product) async { + product.status = ProductStatus.pending; + notifyListeners(); + await Future.delayed(const Duration(seconds: 5)); + product.status = ProductStatus.purchased; + notifyListeners(); + await Future.delayed(const Duration(seconds: 5)); + product.status = ProductStatus.purchasable; + notifyListeners(); + } +} diff --git a/in_app_purchases/step_03/app/lib/logic/dash_upgrades.dart b/in_app_purchases/step_03/app/lib/logic/dash_upgrades.dart new file mode 100644 index 0000000000..826aec517a --- /dev/null +++ b/in_app_purchases/step_03/app/lib/logic/dash_upgrades.dart @@ -0,0 +1,84 @@ +import 'package:flutter/cupertino.dart'; + +import 'dash_counter.dart'; +import 'firebase_notifier.dart'; + +class DashUpgrades extends ChangeNotifier { + DashCounter counter; + FirebaseNotifier firebaseNotifier; + + DashUpgrades(this.counter, this.firebaseNotifier) { + counter.addListener(_onCounterChange); + _updateUpgradeOptions(); + } + + Upgrade tim = Upgrade(cost: 5, work: 1, count: 0); + + void _onCounterChange() { + if (_updateUpgradeOptions()) notifyListeners(); + } + + bool _updateUpgradeOptions() { + var hasChanges = false; + + hasChanges = _updateUpgrade(tim) | hasChanges; + + return hasChanges; + } + + bool _updateUpgrade(Upgrade upgrade) { + var canBuy = counter.count >= upgrade.cost; + if (canBuy == upgrade.purchasable) return false; + upgrade._purchasable = canBuy; + return true; + } + + void addTim() { + _buy(tim); + } + + void _buy( + Upgrade upgrade, + ) { + if (counter.count < upgrade.cost) return; + + counter.addAutoIncrement( + incrementPerSecond: upgrade.work, + costs: upgrade.cost, + ); + upgrade._increment(); + _updateUpgradeOptions(); + notifyListeners(); + } + + @override + void dispose() { + counter.removeListener(_onCounterChange); + super.dispose(); + } +} + +class Upgrade { + int get cost => _cost; + late int _cost; + + double get work => _work; + late double _work; + + int get count => _count; + late int _count; + + bool get purchasable => _purchasable; + bool _purchasable = false; + + Upgrade({required int cost, required double work, required int count}) { + _cost = cost; + _work = work; + _count = count; + } + + void _increment() { + _count++; + _cost = (_cost * 1.3).ceil(); + } +} diff --git a/in_app_purchases/step_03/app/lib/logic/firebase_notifier.dart b/in_app_purchases/step_03/app/lib/logic/firebase_notifier.dart new file mode 100644 index 0000000000..72c140d0d2 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/logic/firebase_notifier.dart @@ -0,0 +1,81 @@ +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:google_sign_in/google_sign_in.dart'; + +import '../firebase_options.dart'; +import '../model/firebase_state.dart'; + +class FirebaseNotifier extends ChangeNotifier { + bool loggedIn = false; + FirebaseState state = FirebaseState.loading; + bool isLoggingIn = false; + + FirebaseNotifier() { + load(); + } + + late final Completer _isInitialized = Completer(); + + Future get firestore async { + var isInitialized = await _isInitialized.future; + if (!isInitialized) { + throw Exception('Firebase is not initialized'); + } + return FirebaseFirestore.instance; + } + + Future load() async { + try { + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + loggedIn = FirebaseAuth.instance.currentUser != null; + state = FirebaseState.available; + _isInitialized.complete(true); + notifyListeners(); + } catch (e) { + state = FirebaseState.notAvailable; + _isInitialized.complete(false); + notifyListeners(); + } + } + + Future login() async { + isLoggingIn = true; + notifyListeners(); + // Trigger the authentication flow + try { + final googleUser = await GoogleSignIn().signIn(); + if (googleUser == null) { + isLoggingIn = false; + notifyListeners(); + return; + } + + // Obtain the auth details from the request + final googleAuth = await googleUser.authentication; + + // Create a new credential + final credential = GoogleAuthProvider.credential( + accessToken: googleAuth.accessToken, + idToken: googleAuth.idToken, + ); + + // Once signed in, return the UserCredential + await FirebaseAuth.instance.signInWithCredential(credential); + + loggedIn = true; + isLoggingIn = false; + notifyListeners(); + } catch (e) { + isLoggingIn = false; + notifyListeners(); + return; + } + } +} diff --git a/in_app_purchases/step_03/app/lib/main.dart b/in_app_purchases/step_03/app/lib/main.dart new file mode 100644 index 0000000000..0a85a4ae74 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/main.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'logic/dash_counter.dart'; +import 'logic/dash_purchases.dart'; +import 'logic/dash_upgrades.dart'; +import 'logic/firebase_notifier.dart'; +import 'pages/home_page.dart'; +import 'pages/purchase_page.dart'; +import 'repo/iap_repo.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Dash Clicker', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const MyHomePage(title: 'Dash Clicker'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + final String title; + + @override + State createState() => _MyHomePageState(); +} + +typedef PageBuilder = Widget Function(); + +class _MyHomePageState extends State { + int _selectedIndex = 0; + + static final List _widgetOptions = [ + const HomePage(), + const PurchasePage(), + ]; + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (_) => FirebaseNotifier()), + ChangeNotifierProvider(create: (_) => DashCounter()), + ChangeNotifierProvider( + create: (context) => DashUpgrades( + context.read(), + context.read(), + ), + ), + ChangeNotifierProvider( + create: (context) => IAPRepo(context.read()), + ), + ChangeNotifierProvider( + create: (context) => DashPurchases( + context.read(), + ), + ), + ], + child: Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: _widgetOptions[_selectedIndex], + bottomNavigationBar: BottomNavigationBar( + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Icons.shop), + label: 'Purchase', + ), + ], + currentIndex: _selectedIndex, + selectedItemColor: Colors.amber[800], + onTap: (index) => setState(() => _selectedIndex = index), + ), + ), + ); + } +} diff --git a/in_app_purchases/step_03/app/lib/model/firebase_state.dart b/in_app_purchases/step_03/app/lib/model/firebase_state.dart new file mode 100644 index 0000000000..1a04db5438 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/model/firebase_state.dart @@ -0,0 +1,5 @@ +enum FirebaseState { + loading, + available, + notAvailable, +} diff --git a/in_app_purchases/step_03/app/lib/model/past_purchase.dart b/in_app_purchases/step_03/app/lib/model/past_purchase.dart new file mode 100644 index 0000000000..42f412481d --- /dev/null +++ b/in_app_purchases/step_03/app/lib/model/past_purchase.dart @@ -0,0 +1,74 @@ +import 'package:flutter/widgets.dart'; + +import '../constants.dart'; + +enum PurchaseType { + subscriptionPurchase, + nonSubscriptionPurchase, +} + +enum Store { + googlePlay, + appStore, +} + +enum Status { + pending, + completed, + active, + expired, +} + +@immutable +class PastPurchase { + final PurchaseType type; + final Store store; + final String orderId; + final String productId; + final DateTime purchaseDate; + final DateTime? expiryDate; + final Status status; + + String get title { + return switch (productId) { + storeKeyConsumable => 'Consumable', + storeKeySubscription => 'Subscription', + _ => productId + }; + } + + PastPurchase.fromJson(Map json) + : type = _typeFromString(json['type'] as String), + store = _storeFromString(json['iapSource'] as String), + orderId = json['orderId'] as String, + productId = json['productId'] as String, + purchaseDate = DateTime.now(), + expiryDate = null, + status = _statusFromString(json['status'] as String); +} + +PurchaseType _typeFromString(String type) { + return switch (type) { + 'nonSubscription' => PurchaseType.subscriptionPurchase, + 'subscription' => PurchaseType.nonSubscriptionPurchase, + _ => throw ArgumentError.value(type, '$type is not a supported type') + }; +} + +Store _storeFromString(String store) { + return switch (store) { + 'googleplay' => Store.googlePlay, + 'appstore' => Store.appStore, + _ => throw ArgumentError.value(store, '$store is not a supported store') + }; +} + +Status _statusFromString(String status) { + return switch (status) { + 'pending' => Status.pending, + 'completed' => Status.completed, + 'active' => Status.active, + 'expired' => Status.expired, + _ => throw ArgumentError.value(status, '$status is not a supported status') + }; +} diff --git a/in_app_purchases/step_03/app/lib/model/purchasable_product.dart b/in_app_purchases/step_03/app/lib/model/purchasable_product.dart new file mode 100644 index 0000000000..873e488056 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/model/purchasable_product.dart @@ -0,0 +1,15 @@ +enum ProductStatus { + purchasable, + purchased, + pending, +} + +class PurchasableProduct { + final String title; + final String description; + final String price; + ProductStatus status; + + PurchasableProduct(this.title, this.description, this.price) + : status = ProductStatus.purchasable; +} diff --git a/in_app_purchases/step_03/app/lib/model/store_state.dart b/in_app_purchases/step_03/app/lib/model/store_state.dart new file mode 100644 index 0000000000..a4f1490b60 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/model/store_state.dart @@ -0,0 +1,5 @@ +enum StoreState { + loading, + available, + notAvailable, +} diff --git a/in_app_purchases/step_03/app/lib/pages/home_page.dart b/in_app_purchases/step_03/app/lib/pages/home_page.dart new file mode 100644 index 0000000000..57087c5f24 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/pages/home_page.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; + +import '../logic/dash_counter.dart'; +import '../logic/dash_purchases.dart'; +import '../logic/dash_upgrades.dart'; + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + return const Column( + children: [ + Expanded( + flex: 2, + child: DashClickerWidget(), + ), + Expanded(child: UpgradeList()), + ], + ); + } +} + +class DashClickerWidget extends StatelessWidget { + const DashClickerWidget({super.key}); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const CounterStateWidget(), + InkWell( + // Don't listen as we don't need a rebuild when the count changes + onTap: Provider.of(context, listen: false).increment, + child: Image.asset(context.read().beautifiedDash + ? 'assets/dash.png' + : 'assets/dash_old.png'), + ) + ], + ), + ); + } +} + +class CounterStateWidget extends StatelessWidget { + const CounterStateWidget({super.key}); + + @override + Widget build(BuildContext context) { + // This widget is the only widget that directly listens to the counter + // and is thus updated almost every frame. Keep this as small as possible. + var counter = context.watch(); + return RichText( + text: TextSpan( + text: 'You have tapped Dash ', + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: counter.countString, + style: const TextStyle(fontWeight: FontWeight.bold)), + const TextSpan(text: ' times!'), + ], + ), + ); + } +} + +class UpgradeList extends StatelessWidget { + const UpgradeList({super.key}); + + @override + Widget build(BuildContext context) { + var upgrades = context.watch(); + return ListView(children: [ + _UpgradeWidget( + upgrade: upgrades.tim, + title: 'Tim Sneath', + onPressed: upgrades.addTim, + ), + ]); + } +} + +class _UpgradeWidget extends StatelessWidget { + final Upgrade upgrade; + final String title; + final VoidCallback onPressed; + + const _UpgradeWidget({ + required this.upgrade, + required this.title, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onPressed, + child: ListTile( + leading: Center( + widthFactor: 1, + child: Text( + upgrade.count.toString(), + ), + ), + title: Text( + title, + style: !upgrade.purchasable + ? const TextStyle(color: Colors.redAccent) + : null, + ), + subtitle: Text('Produces ${upgrade.work} dashes per second'), + trailing: Text( + '${NumberFormat.compact().format(upgrade.cost)} dashes', + ), + )); + } +} diff --git a/in_app_purchases/step_03/app/lib/pages/login_page.dart b/in_app_purchases/step_03/app/lib/pages/login_page.dart new file mode 100644 index 0000000000..f0bc7dbc45 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/pages/login_page.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../logic/firebase_notifier.dart'; + +class LoginPage extends StatelessWidget { + const LoginPage({super.key}); + + @override + Widget build(BuildContext context) { + var firebaseNotifier = context.watch(); + + if (firebaseNotifier.isLoggingIn) { + return const Center( + child: Text('Logging in...'), + ); + } + return Center( + child: FilledButton( + onPressed: () { + firebaseNotifier.login(); + }, + child: const Text('Login'), + )); + } +} diff --git a/in_app_purchases/step_03/app/lib/pages/purchase_page.dart b/in_app_purchases/step_03/app/lib/pages/purchase_page.dart new file mode 100644 index 0000000000..7b4c011173 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/pages/purchase_page.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../logic/dash_purchases.dart'; +import '../model/purchasable_product.dart'; +import '../model/store_state.dart'; +import '../repo/iap_repo.dart'; + +class PurchasePage extends StatelessWidget { + const PurchasePage({super.key}); + + @override + Widget build(BuildContext context) { + var upgrades = context.watch(); + + Widget storeWidget; + switch (upgrades.storeState) { + case StoreState.loading: + storeWidget = _PurchasesLoading(); + case StoreState.available: + storeWidget = _PurchaseList(); + case StoreState.notAvailable: + storeWidget = _PurchasesNotAvailable(); + } + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + storeWidget, + const Padding( + padding: EdgeInsets.fromLTRB(32.0, 32.0, 32.0, 0.0), + child: Text( + 'Past purchases', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + const PastPurchasesWidget(), + ]); + } +} + +class _PurchasesLoading extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const Center(child: Text('Store is loading')); + } +} + +class _PurchasesNotAvailable extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const Center(child: Text('Store not available')); + } +} + +class _PurchaseList extends StatelessWidget { + @override + Widget build(BuildContext context) { + var purchases = context.watch(); + var products = purchases.products; + return Column( + children: products + .map((product) => _PurchaseWidget( + product: product, + onPressed: () { + purchases.buy(product); + })) + .toList(), + ); + } +} + +class _PurchaseWidget extends StatelessWidget { + final PurchasableProduct product; + final VoidCallback onPressed; + + const _PurchaseWidget({ + required this.product, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + var title = product.title; + if (product.status == ProductStatus.purchased) { + title += ' (purchased)'; + } + return InkWell( + onTap: onPressed, + child: ListTile( + title: Text( + title, + ), + subtitle: Text(product.description), + trailing: Text(_trailing()), + )); + } + + String _trailing() { + return switch (product.status) { + ProductStatus.purchasable => product.price, + ProductStatus.purchased => 'purchased', + ProductStatus.pending => 'buying...' + }; + } +} + +class PastPurchasesWidget extends StatelessWidget { + const PastPurchasesWidget({super.key}); + + @override + Widget build(BuildContext context) { + var purchases = context.watch().purchases; + return ListView.separated( + shrinkWrap: true, + itemCount: purchases.length, + itemBuilder: (context, index) => ListTile( + title: Text(purchases[index].title), + subtitle: Text(purchases[index].status.toString()), + ), + separatorBuilder: (context, index) => const Divider(), + ); + } +} diff --git a/in_app_purchases/step_03/app/lib/repo/iap_repo.dart b/in_app_purchases/step_03/app/lib/repo/iap_repo.dart new file mode 100644 index 0000000000..f238a98c39 --- /dev/null +++ b/in_app_purchases/step_03/app/lib/repo/iap_repo.dart @@ -0,0 +1,78 @@ +import 'dart:async'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/cupertino.dart'; + +import '../constants.dart'; +import '../logic/firebase_notifier.dart'; +import '../model/past_purchase.dart'; + +class IAPRepo extends ChangeNotifier { + late FirebaseFirestore _firestore; + late FirebaseAuth _auth; + + bool get isLoggedIn => _user != null; + User? _user; + bool hasActiveSubscription = false; + bool hasUpgrade = false; + List purchases = []; + + StreamSubscription? _userSubscription; + StreamSubscription? _purchaseSubscription; + + IAPRepo(FirebaseNotifier firebaseNotifier) { + firebaseNotifier.firestore.then((value) { + _auth = FirebaseAuth.instance; + _firestore = value; + updatePurchases(); + listenToLogin(); + }); + } + + void listenToLogin() { + _user = _auth.currentUser; + _userSubscription = FirebaseAuth.instance.authStateChanges().listen((user) { + _user = user; + updatePurchases(); + }); + } + + void updatePurchases() { + _purchaseSubscription?.cancel(); + var user = _user; + if (user == null) { + purchases = []; + hasActiveSubscription = false; + hasUpgrade = false; + return; + } + var purchaseStream = _firestore + .collection('purchases') + .where('userId', isEqualTo: user.uid) + .snapshots(); + _purchaseSubscription = purchaseStream.listen((snapshot) { + purchases = snapshot.docs.map((document) { + var data = document.data(); + return PastPurchase.fromJson(data); + }).toList(); + + hasActiveSubscription = purchases.any((element) => + element.productId == storeKeySubscription && + element.status != Status.expired); + + hasUpgrade = purchases.any( + (element) => element.productId == storeKeyUpgrade, + ); + + notifyListeners(); + }); + } + + @override + void dispose() { + _userSubscription?.cancel(); + _purchaseSubscription?.cancel(); + super.dispose(); + } +} diff --git a/in_app_purchases/step_03/app/pubspec.yaml b/in_app_purchases/step_03/app/pubspec.yaml new file mode 100644 index 0000000000..b2920aa5c5 --- /dev/null +++ b/in_app_purchases/step_03/app/pubspec.yaml @@ -0,0 +1,32 @@ +name: dashclicker +description: A codelab for in-app purchases +publish_to: 'none' +version: 0.1.0 + +environment: + sdk: ^3.6.1 + +dependencies: + flutter: + sdk: flutter + cloud_firestore: ^5.6.2 + cupertino_icons: ^1.0.8 + firebase_auth: ^5.4.1 + firebase_core: ^3.10.1 + google_sign_in: ^6.2.2 + http: ^1.3.0 + intl: ^0.20.1 + provider: ^6.1.2 + in_app_purchase: ^3.2.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + in_app_purchase_platform_interface: ^1.4.0 + +flutter: + uses-material-design: true + + assets: + - assets/ diff --git a/in_app_purchases/step_03/app/test/widget_test.dart b/in_app_purchases/step_03/app/test/widget_test.dart new file mode 100644 index 0000000000..35b7eae248 --- /dev/null +++ b/in_app_purchases/step_03/app/test/widget_test.dart @@ -0,0 +1,9 @@ +import 'package:dashclicker/main.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('App starts', (tester) async { + await tester.pumpWidget(const MyApp()); + expect(find.text('Tim Sneath'), findsOneWidget); + }); +} diff --git a/in_app_purchases/step_03/dart-backend/.dockerignore b/in_app_purchases/step_03/dart-backend/.dockerignore new file mode 100644 index 0000000000..21504f8fec --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/.dockerignore @@ -0,0 +1,9 @@ +.dockerignore +Dockerfile +build/ +.dart_tool/ +.git/ +.github/ +.gitignore +.idea/ +.packages diff --git a/in_app_purchases/step_03/dart-backend/.gcloudignore b/in_app_purchases/step_03/dart-backend/.gcloudignore new file mode 100644 index 0000000000..fdf59221d9 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/.gcloudignore @@ -0,0 +1,8 @@ +# Defines what ignore for gcloud deploy + +# Files and directories created by pub. +.dart_tool/ +.packages + +# Conventional directory for build output. +build/ diff --git a/in_app_purchases/step_03/dart-backend/.gitignore b/in_app_purchases/step_03/dart-backend/.gitignore new file mode 100644 index 0000000000..f7f0055f83 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/.gitignore @@ -0,0 +1,11 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Conventional directory for build output. +build/ + +# Keys +assets/*.p8 +assets/appstore.token +assets/service-account*.json diff --git a/in_app_purchases/step_03/dart-backend/CHANGELOG.md b/in_app_purchases/step_03/dart-backend/CHANGELOG.md new file mode 100644 index 0000000000..effe43c82c --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/in_app_purchases/step_03/dart-backend/Dockerfile b/in_app_purchases/step_03/dart-backend/Dockerfile new file mode 100644 index 0000000000..c333dee714 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/Dockerfile @@ -0,0 +1,21 @@ +# Use latest stable channel SDK. +FROM dart:stable AS build + +# Resolve app dependencies. +WORKDIR /app +COPY pubspec.* ./ +RUN dart pub get + +# Copy app source code (except anything in .dockerignore) and AOT compile app. +COPY . . +RUN dart compile exe bin/server.dart -o bin/server + +# Build minimal serving image from AOT-compiled `/server` +# and the pre-built AOT-runtime in the `/runtime/` directory of the base image. +FROM scratch +COPY --from=build /runtime/ / +COPY --from=build /app/bin/server /app/bin/ + +# Start server. +EXPOSE 8080 +CMD ["/app/bin/server"] diff --git a/in_app_purchases/step_03/dart-backend/README.md b/in_app_purchases/step_03/dart-backend/README.md new file mode 100644 index 0000000000..e37073b098 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/README.md @@ -0,0 +1,21 @@ +### Configuration Files + +Create `lib/constants.dart` with: + +``` +const androidPackageId = "your Android package ID"; +const appStoreIssuerId = 'App Store Key issuer ID'; +const appStoreKeyId = 'App Store Keu ID'; +const appStoreSharedSecret = "App Store shared secret"; +const bundleId = 'your iOS bundle ID'; +const googlePlayProjectName = "Google Cloud project name"; +const googlePlayPubsubBillingTopic = "play_billing"; // change if necessary +``` + +- Add `assets/service-account-firebase.json` with the server key for the Firebase Firestore project. +- Add `assets/service-account-google-play.json` with the server key for the Google Play access key project. +- Add `assets/SubscriptionKey.p8` with the server key for the App Store. + +### Running + +Run locally with `dart run ./bin/server.dart`. diff --git a/in_app_purchases/step_03/dart-backend/analysis_options.yaml b/in_app_purchases/step_03/dart-backend/analysis_options.yaml new file mode 100644 index 0000000000..951da71bfe --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/analysis_options.yaml @@ -0,0 +1,22 @@ +include: package:lints/recommended.yaml + +analyzer: + language: + strict-casts: true + strict-inference: true + +linter: + rules: + avoid_types_on_closure_parameters: true + avoid_void_async: true + cancel_subscriptions: true + close_sinks: true + directives_ordering: true + package_prefixed_library_names: true + prefer_relative_imports: true + prefer_single_quotes: true + test_types_in_equals: true + throw_in_finally: true + unawaited_futures: true + unnecessary_statements: true + use_super_parameters: true diff --git a/in_app_purchases/step_03/dart-backend/assets/README.md b/in_app_purchases/step_03/dart-backend/assets/README.md new file mode 100644 index 0000000000..457c2b6889 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/assets/README.md @@ -0,0 +1,4 @@ +Copy here the service account keys for Firebase and Google Play as: + +- `service-account-firebase.json`: With permissions for Firestore +- `service-account-google-play.json`: with permissions for Google Play Developer API diff --git a/in_app_purchases/step_03/dart-backend/bin/server.dart b/in_app_purchases/step_03/dart-backend/bin/server.dart new file mode 100644 index 0000000000..693d011273 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/bin/server.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:firebase_backend_dart/helpers.dart'; +import 'package:shelf_router/shelf_router.dart'; + +Future main() async { + final router = Router(); + + // Start service + await serveHandler(router.call); +} diff --git a/in_app_purchases/step_03/dart-backend/lib/constants.dart b/in_app_purchases/step_03/dart-backend/lib/constants.dart new file mode 100644 index 0000000000..8144599886 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/lib/constants.dart @@ -0,0 +1,7 @@ +const androidPackageId = 'your Android package ID'; +const appStoreIssuerId = 'App Store Key issuer ID'; +const appStoreKeyId = 'App Store Key ID'; +const appStoreSharedSecret = 'App Store shared secret'; +const bundleId = 'your iOS bundle ID'; +const googlePlayProjectName = 'Google Cloud project name'; +const googlePlayPubsubBillingTopic = 'play_billing'; diff --git a/in_app_purchases/step_03/dart-backend/lib/helpers.dart b/in_app_purchases/step_03/dart-backend/lib/helpers.dart new file mode 100644 index 0000000000..0a5fa756c2 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/lib/helpers.dart @@ -0,0 +1,75 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; + +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart'; + +/// Serves [handler] on [InternetAddress.anyIPv4] using the port returned by +/// [listenPort]. +/// +/// The returned [Future] will complete using [terminateRequestFuture] after +/// closing the server. +Future serveHandler(Handler handler) async { + final port = listenPort(); + + final server = await serve( + handler, + InternetAddress.anyIPv4, // Allows external connections + port, + ); + print('Serving at http://${server.address.host}:${server.port}'); + + await terminateRequestFuture(); + + await server.close(); +} + +/// Returns the port to listen on from environment variable or uses the default +/// `8080`. +/// +/// See https://cloud.google.com/run/docs/reference/container-contract#port +int listenPort() => int.parse(Platform.environment['PORT'] ?? '8080'); + +/// Returns a [Future] that completes when the process receives a +/// [ProcessSignal] requesting a shutdown. +/// +/// [ProcessSignal.sigint] is listened to on all platforms. +/// +/// [ProcessSignal.sigterm] is listened to on all platforms except Windows. +Future terminateRequestFuture() { + final completer = Completer(); + + StreamSubscription? sigIntSub, sigTermSub; + + Future signalHandler(ProcessSignal signal) async { + print('Received signal $signal - closing'); + + if (sigIntSub != null) { + await sigIntSub!.cancel(); + sigIntSub = null; + } + + if (sigTermSub != null) { + await sigTermSub!.cancel(); + sigTermSub = null; + } + + if (!completer.isCompleted) { + completer.complete(true); + } + } + + sigIntSub = ProcessSignal.sigint.watch().listen(signalHandler); + + // SIGTERM is not supported on Windows. Attempting to register a SIGTERM + // handler raises an exception. + if (!Platform.isWindows) { + sigTermSub = ProcessSignal.sigterm.watch().listen(signalHandler); + } + + return completer.future; +} diff --git a/in_app_purchases/step_03/dart-backend/lib/iap_repository.dart b/in_app_purchases/step_03/dart-backend/lib/iap_repository.dart new file mode 100644 index 0000000000..1856f31c95 --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/lib/iap_repository.dart @@ -0,0 +1,230 @@ +import 'package:googleapis/firestore/v1.dart'; + +import 'products.dart'; + +enum IAPSource { + googleplay, + appstore, +} + +abstract class Purchase { + final IAPSource iapSource; + final String orderId; + final String productId; + final String? userId; + final DateTime purchaseDate; + final ProductType type; + + const Purchase({ + required this.iapSource, + required this.orderId, + required this.productId, + required this.userId, + required this.purchaseDate, + required this.type, + }); + + Map toDocument() { + return { + 'iapSource': Value(stringValue: iapSource.name), + 'orderId': Value(stringValue: orderId), + 'productId': Value(stringValue: productId), + 'userId': Value(stringValue: userId), + 'purchaseDate': + Value(timestampValue: purchaseDate.toUtc().toIso8601String()), + 'type': Value(stringValue: type.name), + }; + } + + Map updateDocument(); + + static Purchase fromDocument(Document e) { + final type = ProductType.values.firstWhere( + (element) => element.name == e.fields!['type']!.stringValue); + switch (type) { + case ProductType.subscription: + return SubscriptionPurchase( + iapSource: e.fields!['iapSource']!.stringValue == 'googleplay' + ? IAPSource.googleplay + : IAPSource.appstore, + orderId: e.fields!['orderId']!.stringValue!, + productId: e.fields!['productId']!.stringValue!, + userId: e.fields!['userId']!.stringValue, + purchaseDate: + DateTime.parse(e.fields!['purchaseDate']!.timestampValue!), + status: SubscriptionStatus.values.firstWhere( + (element) => element.name == e.fields!['status']!.stringValue), + expiryDate: DateTime.tryParse( + e.fields!['expiryDate']?.timestampValue ?? '') ?? + DateTime.now(), + ); + case ProductType.nonSubscription: + return NonSubscriptionPurchase( + iapSource: e.fields!['iapSource']!.stringValue == 'googleplay' + ? IAPSource.googleplay + : IAPSource.appstore, + orderId: e.fields!['orderId']!.stringValue!, + productId: e.fields!['productId']!.stringValue!, + userId: e.fields!['userId']!.stringValue, + purchaseDate: + DateTime.parse(e.fields!['purchaseDate']!.timestampValue!), + status: NonSubscriptionStatus.values.firstWhere( + (element) => element.name == e.fields!['status']!.stringValue), + ); + } + } +} + +enum NonSubscriptionStatus { + pending, + completed, + cancelled, +} + +enum SubscriptionStatus { pending, active, expired } + +class NonSubscriptionPurchase extends Purchase { + final NonSubscriptionStatus status; + + NonSubscriptionPurchase({ + required super.iapSource, + required super.orderId, + required super.productId, + required super.userId, + required super.purchaseDate, + required this.status, + super.type = ProductType.nonSubscription, + }); + + @override + Map toDocument() { + final doc = super.toDocument(); + doc.addAll({ + 'status': Value(stringValue: status.name), + }); + return doc; + } + + @override + Map updateDocument() { + return { + 'status': Value(stringValue: status.name), + }; + } + + @override + String toString() { + return 'NonSubscriptionPurchase { ' + 'iapSource: $iapSource, ' + 'orderId: $orderId, ' + 'productId: $productId, ' + 'userId: $userId, ' + 'purchaseDate: $purchaseDate, ' + 'status: $status, ' + 'type: $type ' + '}'; + } +} + +class SubscriptionPurchase extends Purchase { + final SubscriptionStatus status; + final DateTime expiryDate; + + SubscriptionPurchase({ + required super.iapSource, + required super.orderId, + required super.productId, + required super.userId, + required super.purchaseDate, + required this.status, + required this.expiryDate, + super.type = ProductType.subscription, + }); + + @override + Map toDocument() { + final doc = super.toDocument(); + doc.addAll({ + 'expiryDate': Value(timestampValue: expiryDate.toUtc().toIso8601String()), + 'status': Value(stringValue: status.name), + }); + return doc; + } + + @override + Map updateDocument() { + return { + 'status': Value(stringValue: status.name), + }; + } + + @override + String toString() { + return 'SubscriptionPurchase { ' + 'iapSource: $iapSource, ' + 'orderId: $orderId, ' + 'productId: $productId, ' + 'userId: $userId, ' + 'purchaseDate: $purchaseDate, ' + 'status: $status, ' + 'expiryDate: $expiryDate, ' + 'type: $type ' + '}'; + } +} + +class IapRepository { + final FirestoreApi api; + final String projectId; + + IapRepository(this.api, this.projectId); + + Future createOrUpdatePurchase(Purchase purchaseData) async { + print('Updating $purchaseData'); + final purchaseId = _purchaseId(purchaseData); + await api.projects.databases.documents.commit( + CommitRequest( + writes: [ + Write( + update: Document( + fields: purchaseData.toDocument(), + name: + 'projects/$projectId/databases/(default)/documents/purchases/$purchaseId'), + ), + ], + ), + 'projects/$projectId/databases/(default)', + ); + } + + Future updatePurchase(Purchase purchaseData) async { + print('Updating $purchaseData'); + final purchaseId = _purchaseId(purchaseData); + await api.projects.databases.documents.commit( + CommitRequest( + writes: [ + Write( + update: Document( + fields: purchaseData.updateDocument(), + name: + 'projects/$projectId/databases/(default)/documents/purchases/$purchaseId'), + updateMask: DocumentMask(fieldPaths: ['status']), + ), + ], + ), + 'projects/$projectId/databases/(default)', + ); + } + + String _purchaseId(Purchase purchaseData) { + return '${purchaseData.iapSource.name}_${purchaseData.orderId}'; + } + + Future> getPurchases() async { + final list = await api.projects.databases.documents.list( + 'projects/$projectId/databases/(default)/documents', + 'purchases', + ); + return list.documents!.map((e) => Purchase.fromDocument(e)).toList(); + } +} diff --git a/in_app_purchases/step_03/dart-backend/lib/products.dart b/in_app_purchases/step_03/dart-backend/lib/products.dart new file mode 100644 index 0000000000..d6baaeb9ca --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/lib/products.dart @@ -0,0 +1,26 @@ +class ProductData { + final String productId; + final ProductType type; + + const ProductData(this.productId, this.type); +} + +enum ProductType { + subscription, + nonSubscription, +} + +const productDataMap = { + 'dash_consumable_2k': ProductData( + 'dash_consumable_2k', + ProductType.nonSubscription, + ), + 'dash_upgrade_3d': ProductData( + 'dash_upgrade_3d', + ProductType.nonSubscription, + ), + 'dash_subscription_doubler': ProductData( + 'dash_subscription_doubler', + ProductType.subscription, + ), +}; diff --git a/in_app_purchases/step_03/dart-backend/pubspec.yaml b/in_app_purchases/step_03/dart-backend/pubspec.yaml new file mode 100644 index 0000000000..da8365432a --- /dev/null +++ b/in_app_purchases/step_03/dart-backend/pubspec.yaml @@ -0,0 +1,19 @@ +name: firebase_backend_dart +description: A server app using the shelf package and Docker. +version: 1.0.0 +# repository: https://github.com/my_org/my_repo + +environment: + sdk: ^3.6.1 + +dependencies: + app_store_server_sdk: ^1.2.9 + googleapis: ^13.2.0 + googleapis_auth: ^1.6.0 + http: ^1.3.0 + shelf: ^1.4.0 + shelf_router: ^1.1.0 + +dev_dependencies: + lints: ^5.0.0 + test: ^1.24.0 diff --git a/in_app_purchases/step_07/app/.gitignore b/in_app_purchases/step_07/app/.gitignore index 6f1f0459d5..9b7a64b527 100644 --- a/in_app_purchases/step_07/app/.gitignore +++ b/in_app_purchases/step_07/app/.gitignore @@ -48,4 +48,3 @@ app.*.map.json GoogleService-Info.plist google-services.json ios/firebase_app_id_file.json -lib/firebase_options.dart diff --git a/in_app_purchases/step_07/app/ios/Runner.xcodeproj/project.pbxproj b/in_app_purchases/step_07/app/ios/Runner.xcodeproj/project.pbxproj index e437d48aa4..6f2fb5b153 100644 --- a/in_app_purchases/step_07/app/ios/Runner.xcodeproj/project.pbxproj +++ b/in_app_purchases/step_07/app/ios/Runner.xcodeproj/project.pbxproj @@ -362,7 +362,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -542,7 +541,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -565,7 +563,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/in_app_purchases/step_07/app/pubspec.yaml b/in_app_purchases/step_07/app/pubspec.yaml index 682968a175..b2920aa5c5 100644 --- a/in_app_purchases/step_07/app/pubspec.yaml +++ b/in_app_purchases/step_07/app/pubspec.yaml @@ -4,26 +4,26 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: flutter: sdk: flutter - cloud_firestore: ^5.5.1 + cloud_firestore: ^5.6.2 cupertino_icons: ^1.0.8 - firebase_auth: ^5.3.4 - firebase_core: ^3.8.1 + firebase_auth: ^5.4.1 + firebase_core: ^3.10.1 google_sign_in: ^6.2.2 - http: ^1.2.2 - in_app_purchase: ^3.2.0 - in_app_purchase_platform_interface: ^1.4.0 + http: ^1.3.0 intl: ^0.20.1 provider: ^6.1.2 + in_app_purchase: ^3.2.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^5.0.0 + in_app_purchase_platform_interface: ^1.4.0 flutter: uses-material-design: true diff --git a/in_app_purchases/step_07/dart-backend/.gitignore b/in_app_purchases/step_07/dart-backend/.gitignore index 49d9e600e4..f7f0055f83 100644 --- a/in_app_purchases/step_07/dart-backend/.gitignore +++ b/in_app_purchases/step_07/dart-backend/.gitignore @@ -9,4 +9,3 @@ build/ assets/*.p8 assets/appstore.token assets/service-account*.json -lib/constants.dart diff --git a/in_app_purchases/step_07/dart-backend/pubspec.yaml b/in_app_purchases/step_07/dart-backend/pubspec.yaml index 7a168d9aee..da8365432a 100644 --- a/in_app_purchases/step_07/dart-backend/pubspec.yaml +++ b/in_app_purchases/step_07/dart-backend/pubspec.yaml @@ -4,13 +4,13 @@ version: 1.0.0 # repository: https://github.com/my_org/my_repo environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: app_store_server_sdk: ^1.2.9 googleapis: ^13.2.0 googleapis_auth: ^1.6.0 - http: ^1.2.2 + http: ^1.3.0 shelf: ^1.4.0 shelf_router: ^1.1.0 diff --git a/in_app_purchases/step_08/app/.gitignore b/in_app_purchases/step_08/app/.gitignore index 6f1f0459d5..9b7a64b527 100644 --- a/in_app_purchases/step_08/app/.gitignore +++ b/in_app_purchases/step_08/app/.gitignore @@ -48,4 +48,3 @@ app.*.map.json GoogleService-Info.plist google-services.json ios/firebase_app_id_file.json -lib/firebase_options.dart diff --git a/in_app_purchases/step_08/app/ios/Runner.xcodeproj/project.pbxproj b/in_app_purchases/step_08/app/ios/Runner.xcodeproj/project.pbxproj index e437d48aa4..6f2fb5b153 100644 --- a/in_app_purchases/step_08/app/ios/Runner.xcodeproj/project.pbxproj +++ b/in_app_purchases/step_08/app/ios/Runner.xcodeproj/project.pbxproj @@ -362,7 +362,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -542,7 +541,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -565,7 +563,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/in_app_purchases/step_08/app/pubspec.yaml b/in_app_purchases/step_08/app/pubspec.yaml index 682968a175..b2920aa5c5 100644 --- a/in_app_purchases/step_08/app/pubspec.yaml +++ b/in_app_purchases/step_08/app/pubspec.yaml @@ -4,26 +4,26 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: flutter: sdk: flutter - cloud_firestore: ^5.5.1 + cloud_firestore: ^5.6.2 cupertino_icons: ^1.0.8 - firebase_auth: ^5.3.4 - firebase_core: ^3.8.1 + firebase_auth: ^5.4.1 + firebase_core: ^3.10.1 google_sign_in: ^6.2.2 - http: ^1.2.2 - in_app_purchase: ^3.2.0 - in_app_purchase_platform_interface: ^1.4.0 + http: ^1.3.0 intl: ^0.20.1 provider: ^6.1.2 + in_app_purchase: ^3.2.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^5.0.0 + in_app_purchase_platform_interface: ^1.4.0 flutter: uses-material-design: true diff --git a/in_app_purchases/step_08/dart-backend/.gitignore b/in_app_purchases/step_08/dart-backend/.gitignore index 49d9e600e4..f7f0055f83 100644 --- a/in_app_purchases/step_08/dart-backend/.gitignore +++ b/in_app_purchases/step_08/dart-backend/.gitignore @@ -9,4 +9,3 @@ build/ assets/*.p8 assets/appstore.token assets/service-account*.json -lib/constants.dart diff --git a/in_app_purchases/step_08/dart-backend/pubspec.yaml b/in_app_purchases/step_08/dart-backend/pubspec.yaml index 7a168d9aee..da8365432a 100644 --- a/in_app_purchases/step_08/dart-backend/pubspec.yaml +++ b/in_app_purchases/step_08/dart-backend/pubspec.yaml @@ -4,13 +4,13 @@ version: 1.0.0 # repository: https://github.com/my_org/my_repo environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: app_store_server_sdk: ^1.2.9 googleapis: ^13.2.0 googleapis_auth: ^1.6.0 - http: ^1.2.2 + http: ^1.3.0 shelf: ^1.4.0 shelf_router: ^1.1.0 diff --git a/in_app_purchases/step_09/app/.gitignore b/in_app_purchases/step_09/app/.gitignore index 6f1f0459d5..9b7a64b527 100644 --- a/in_app_purchases/step_09/app/.gitignore +++ b/in_app_purchases/step_09/app/.gitignore @@ -48,4 +48,3 @@ app.*.map.json GoogleService-Info.plist google-services.json ios/firebase_app_id_file.json -lib/firebase_options.dart diff --git a/in_app_purchases/step_09/app/ios/Runner.xcodeproj/project.pbxproj b/in_app_purchases/step_09/app/ios/Runner.xcodeproj/project.pbxproj index e437d48aa4..6f2fb5b153 100644 --- a/in_app_purchases/step_09/app/ios/Runner.xcodeproj/project.pbxproj +++ b/in_app_purchases/step_09/app/ios/Runner.xcodeproj/project.pbxproj @@ -362,7 +362,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -542,7 +541,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -565,7 +563,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/in_app_purchases/step_09/app/pubspec.yaml b/in_app_purchases/step_09/app/pubspec.yaml index 682968a175..b2920aa5c5 100644 --- a/in_app_purchases/step_09/app/pubspec.yaml +++ b/in_app_purchases/step_09/app/pubspec.yaml @@ -4,26 +4,26 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: flutter: sdk: flutter - cloud_firestore: ^5.5.1 + cloud_firestore: ^5.6.2 cupertino_icons: ^1.0.8 - firebase_auth: ^5.3.4 - firebase_core: ^3.8.1 + firebase_auth: ^5.4.1 + firebase_core: ^3.10.1 google_sign_in: ^6.2.2 - http: ^1.2.2 - in_app_purchase: ^3.2.0 - in_app_purchase_platform_interface: ^1.4.0 + http: ^1.3.0 intl: ^0.20.1 provider: ^6.1.2 + in_app_purchase: ^3.2.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^5.0.0 + in_app_purchase_platform_interface: ^1.4.0 flutter: uses-material-design: true diff --git a/in_app_purchases/step_09/dart-backend/.gitignore b/in_app_purchases/step_09/dart-backend/.gitignore index 49d9e600e4..f7f0055f83 100644 --- a/in_app_purchases/step_09/dart-backend/.gitignore +++ b/in_app_purchases/step_09/dart-backend/.gitignore @@ -9,4 +9,3 @@ build/ assets/*.p8 assets/appstore.token assets/service-account*.json -lib/constants.dart diff --git a/in_app_purchases/step_09/dart-backend/pubspec.yaml b/in_app_purchases/step_09/dart-backend/pubspec.yaml index 7a168d9aee..da8365432a 100644 --- a/in_app_purchases/step_09/dart-backend/pubspec.yaml +++ b/in_app_purchases/step_09/dart-backend/pubspec.yaml @@ -4,13 +4,13 @@ version: 1.0.0 # repository: https://github.com/my_org/my_repo environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: app_store_server_sdk: ^1.2.9 googleapis: ^13.2.0 googleapis_auth: ^1.6.0 - http: ^1.2.2 + http: ^1.3.0 shelf: ^1.4.0 shelf_router: ^1.1.0 diff --git a/in_app_purchases/step_10/app/.gitignore b/in_app_purchases/step_10/app/.gitignore index 6f1f0459d5..9b7a64b527 100644 --- a/in_app_purchases/step_10/app/.gitignore +++ b/in_app_purchases/step_10/app/.gitignore @@ -48,4 +48,3 @@ app.*.map.json GoogleService-Info.plist google-services.json ios/firebase_app_id_file.json -lib/firebase_options.dart diff --git a/in_app_purchases/step_10/app/ios/Runner.xcodeproj/project.pbxproj b/in_app_purchases/step_10/app/ios/Runner.xcodeproj/project.pbxproj index e437d48aa4..6f2fb5b153 100644 --- a/in_app_purchases/step_10/app/ios/Runner.xcodeproj/project.pbxproj +++ b/in_app_purchases/step_10/app/ios/Runner.xcodeproj/project.pbxproj @@ -362,7 +362,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -542,7 +541,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -565,7 +563,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = TC87DMJLQP; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/in_app_purchases/step_10/app/pubspec.yaml b/in_app_purchases/step_10/app/pubspec.yaml index 682968a175..b2920aa5c5 100644 --- a/in_app_purchases/step_10/app/pubspec.yaml +++ b/in_app_purchases/step_10/app/pubspec.yaml @@ -4,26 +4,26 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: flutter: sdk: flutter - cloud_firestore: ^5.5.1 + cloud_firestore: ^5.6.2 cupertino_icons: ^1.0.8 - firebase_auth: ^5.3.4 - firebase_core: ^3.8.1 + firebase_auth: ^5.4.1 + firebase_core: ^3.10.1 google_sign_in: ^6.2.2 - http: ^1.2.2 - in_app_purchase: ^3.2.0 - in_app_purchase_platform_interface: ^1.4.0 + http: ^1.3.0 intl: ^0.20.1 provider: ^6.1.2 + in_app_purchase: ^3.2.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^5.0.0 + in_app_purchase_platform_interface: ^1.4.0 flutter: uses-material-design: true diff --git a/in_app_purchases/step_10/dart-backend/.gitignore b/in_app_purchases/step_10/dart-backend/.gitignore index 49d9e600e4..f7f0055f83 100644 --- a/in_app_purchases/step_10/dart-backend/.gitignore +++ b/in_app_purchases/step_10/dart-backend/.gitignore @@ -9,4 +9,3 @@ build/ assets/*.p8 assets/appstore.token assets/service-account*.json -lib/constants.dart diff --git a/in_app_purchases/step_10/dart-backend/pubspec.yaml b/in_app_purchases/step_10/dart-backend/pubspec.yaml index 7a168d9aee..da8365432a 100644 --- a/in_app_purchases/step_10/dart-backend/pubspec.yaml +++ b/in_app_purchases/step_10/dart-backend/pubspec.yaml @@ -4,13 +4,13 @@ version: 1.0.0 # repository: https://github.com/my_org/my_repo environment: - sdk: ^3.6.0-0 + sdk: ^3.6.1 dependencies: app_store_server_sdk: ^1.2.9 googleapis: ^13.2.0 googleapis_auth: ^1.6.0 - http: ^1.2.2 + http: ^1.3.0 shelf: ^1.4.0 shelf_router: ^1.1.0