mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-18 00:17:40 +01:00
windows #1
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '13.0'
|
||||
|
||||
platform :ios, '15.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
@@ -7,14 +7,22 @@ PODS:
|
||||
- Flutter (1.0.0)
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- flutter_secure_storage_darwin (10.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- gamepads_ios (0.1.1):
|
||||
- Flutter
|
||||
- image_picker_ios (0.0.1):
|
||||
- Flutter
|
||||
- in_app_purchase_storekit (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- in_app_review (2.0.0):
|
||||
- Flutter
|
||||
- integration_test (0.0.1):
|
||||
- Flutter
|
||||
- ios_receipt (0.0.1):
|
||||
- Flutter
|
||||
- media_key_detector_ios (0.0.1):
|
||||
- Flutter
|
||||
- nsd_ios (0.0.1):
|
||||
@@ -44,10 +52,13 @@ DEPENDENCIES:
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`)
|
||||
- gamepads_ios (from `.symlinks/plugins/gamepads_ios/ios`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`)
|
||||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
- ios_receipt (from `.symlinks/plugins/ios_receipt/ios`)
|
||||
- media_key_detector_ios (from `.symlinks/plugins/media_key_detector_ios/ios`)
|
||||
- nsd_ios (from `.symlinks/plugins/nsd_ios/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
@@ -68,14 +79,20 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter
|
||||
flutter_local_notifications:
|
||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||
flutter_secure_storage_darwin:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage_darwin/darwin"
|
||||
gamepads_ios:
|
||||
:path: ".symlinks/plugins/gamepads_ios/ios"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
in_app_purchase_storekit:
|
||||
:path: ".symlinks/plugins/in_app_purchase_storekit/darwin"
|
||||
in_app_review:
|
||||
:path: ".symlinks/plugins/in_app_review/ios"
|
||||
integration_test:
|
||||
:path: ".symlinks/plugins/integration_test/ios"
|
||||
ios_receipt:
|
||||
:path: ".symlinks/plugins/ios_receipt/ios"
|
||||
media_key_detector_ios:
|
||||
:path: ".symlinks/plugins/media_key_detector_ios/ios"
|
||||
nsd_ios:
|
||||
@@ -102,10 +119,13 @@ SPEC CHECKSUMS:
|
||||
device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f
|
||||
flutter_secure_storage_darwin: 557817588b80e60213cbecb573c45c76b788018d
|
||||
gamepads_ios: 1d2930c7a4450a9a1b57444ebf305a6a6cbeea0b
|
||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||
in_app_purchase_storekit: 2342c0a5da86593124d08dd13d920f39a52b273a
|
||||
in_app_review: 436034b18594851a7328d7f1c2ed5ec235b79cfc
|
||||
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||
ios_receipt: c2d5b4c36953c377a024992393976214ce6951e6
|
||||
media_key_detector_ios: 7ff9aefdfea00bb7b71e184132381b7d0e7e1269
|
||||
nsd_ios: 8c37babdc6538e3350dbed3a52674d2edde98173
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
@@ -117,6 +137,6 @@ SPEC CHECKSUMS:
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
wakelock_plus: 76957ab028e12bfa4e66813c99e46637f367fc7e
|
||||
|
||||
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
|
||||
PODFILE CHECKSUM: 7ebd5c9b932b3af79d5c67e3af873118b74e970f
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -496,6 +496,7 @@
|
||||
DEVELOPMENT_TEAM = UZRHKPVWN9;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -683,6 +684,7 @@
|
||||
DEVELOPMENT_TEAM = UZRHKPVWN9;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -710,6 +712,7 @@
|
||||
DEVELOPMENT_TEAM = UZRHKPVWN9;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bike_control/utils/core.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:version/version.dart';
|
||||
import 'package:windows_iap/windows_iap.dart';
|
||||
|
||||
/// Windows-specific IAP service
|
||||
/// Note: This is a stub implementation. For actual Windows Store integration,
|
||||
@@ -25,6 +28,8 @@ class WindowsIAPService {
|
||||
String? _lastCommandDate;
|
||||
int? _dailyCommandCount;
|
||||
|
||||
final _windowsIapPlugin = WindowsIap();
|
||||
|
||||
WindowsIAPService(this._prefs);
|
||||
|
||||
/// Initialize the Windows IAP service
|
||||
@@ -53,7 +58,7 @@ class WindowsIAPService {
|
||||
_isPurchased = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_windowsIapPlugin.
|
||||
// TODO: Add Windows Store API integration
|
||||
// Check if the app was purchased from the Windows Store
|
||||
// This would require platform channel implementation to call Windows Store APIs
|
||||
@@ -68,11 +73,15 @@ class WindowsIAPService {
|
||||
// IMPORTANT: This assumes the app is currently paid and this update will be released
|
||||
// while the app is still paid. Only users who downloaded the paid version will have
|
||||
// a last_seen_version. After changing the app to free, new users won't have this set.
|
||||
final lastSeenVersion = await _prefs.read(key: 'last_seen_version');
|
||||
final lastSeenVersion = core.settings.getLastSeenVersion();
|
||||
if (lastSeenVersion != null && lastSeenVersion.isNotEmpty) {
|
||||
_isPurchased = true;
|
||||
await _prefs.write(key: _purchaseStatusKey, value: "true");
|
||||
debugPrint('Existing Windows user detected - granting full access');
|
||||
Version lastVersion = Version.parse(lastSeenVersion);
|
||||
// If they had a previous version, they're an existing paid user
|
||||
_isPurchased = lastVersion < Version(4, 2, 0);
|
||||
if (_isPurchased) {
|
||||
await _prefs.write(key: _purchaseStatusKey, value: "true");
|
||||
}
|
||||
debugPrint('Existing Android user detected - granting full access');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Error checking Windows previous version: $e');
|
||||
|
||||
@@ -1657,6 +1657,13 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
windows_iap:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: windows_iap
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -36,6 +36,8 @@ dependencies:
|
||||
path: ios_receipt
|
||||
flutter_secure_storage: ^10.0.0
|
||||
in_app_purchase: ^3.2.1
|
||||
windows_iap:
|
||||
path: windows_iap
|
||||
window_manager: ^0.5.1
|
||||
device_info_plus: ^12.1.0
|
||||
keypress_simulator:
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <universal_ble/universal_ble_plugin_c_api.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
#include <windows_iap/windows_iap_plugin_c_api.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
BluetoothLowEnergyWindowsPluginCApiRegisterWithRegistrar(
|
||||
@@ -44,4 +45,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
WindowManagerPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
|
||||
WindowsIapPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowsIapPluginCApi"));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
universal_ble
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
windows_iap
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
||||
30
windows_iap/.gitignore
vendored
Normal file
30
windows_iap/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
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
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
||||
30
windows_iap/.metadata
Normal file
30
windows_iap/.metadata
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||
- platform: windows
|
||||
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
3
windows_iap/CHANGELOG.md
Normal file
3
windows_iap/CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
1
windows_iap/LICENSE
Normal file
1
windows_iap/LICENSE
Normal file
@@ -0,0 +1 @@
|
||||
TODO: Add your license here.
|
||||
15
windows_iap/README.md
Normal file
15
windows_iap/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# windows_iap
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter
|
||||
[plug-in package](https://flutter.dev/developing-packages/),
|
||||
a specialized package that includes platform-specific implementation code for
|
||||
Android and/or iOS.
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://flutter.dev/docs), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
|
||||
4
windows_iap/analysis_options.yaml
Normal file
4
windows_iap/analysis_options.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
47
windows_iap/example/.gitignore
vendored
Normal file
47
windows_iap/example/.gitignore
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
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
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# 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
|
||||
16
windows_iap/example/README.md
Normal file
16
windows_iap/example/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# windows_iap_example
|
||||
|
||||
Demonstrates how to use the windows_iap plugin.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
||||
29
windows_iap/example/analysis_options.yaml
Normal file
29
windows_iap/example/analysis_options.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
75
windows_iap/example/lib/main.dart
Normal file
75
windows_iap/example/lib/main.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
// ignore_for_file: use_build_context_synchronously, avoid_print
|
||||
|
||||
import 'package:andesgroup_common/common.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:windows_iap/windows_iap.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<MyApp> createState() => _MyAppState();
|
||||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
final _windowsIapPlugin = WindowsIap();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Builder(builder: (context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Plugin example app'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final result = await _windowsIapPlugin.checkPurchase();
|
||||
showAlertDialog(context,
|
||||
content: 'checkPurchase: $result');
|
||||
},
|
||||
child: const Text('checkPurchase')),
|
||||
const Gap(16),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final result =
|
||||
await _windowsIapPlugin.makePurchase('hihi');
|
||||
print('result is $result');
|
||||
},
|
||||
child: const Text('makePurchase')),
|
||||
const Gap(16),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
try {
|
||||
final products = await _windowsIapPlugin.getProducts();
|
||||
print('products: $products');
|
||||
} on PlatformException catch (e, s) {
|
||||
print('error');
|
||||
print(e.toString());
|
||||
}
|
||||
},
|
||||
child: const Text('getProducts')),
|
||||
const Gap(16),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final result = await _windowsIapPlugin.getAddonLicenses();
|
||||
print('licenses: $result');
|
||||
},
|
||||
child: const Text('getAddonLicenses')),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
529
windows_iap/example/pubspec.lock
Normal file
529
windows_iap/example/pubspec.lock
Normal file
@@ -0,0 +1,529 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
andesgroup_common:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: andesgroup_common
|
||||
sha256: e560b64c55e3a8605fc5d2ad789cf625c3124348d48f2549de7bf0aa565df65e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.19"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
cached_network_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image
|
||||
sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.3"
|
||||
cached_network_image_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_platform_interface
|
||||
sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
cached_network_image_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cached_network_image_web
|
||||
sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_blurhash:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_blurhash
|
||||
sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
flutter_cache_manager:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
fluttertoast:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fluttertoast
|
||||
sha256: "7cc92eabe01e3f1babe1571c5560b135dfc762a34e41e9056881e2196b178ec1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.2"
|
||||
freezed_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: freezed_annotation
|
||||
sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.5"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.0"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
keyboard_dismisser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: keyboard_dismisser
|
||||
sha256: f67e032581fc3dd1f77e1cb54c421b089e015d122aeba2490ba001cfcc42a181
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
logger:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "5076f09225f91dc49289a4ccb92df2eeea9ea01cf7c26d49b3a1f04c6a49eec1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: octo_image
|
||||
sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.12"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.27.7"
|
||||
shimmer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shimmer
|
||||
sha256: "1f1009b5845a1f88f1c5630212279540486f97409e9fc3f63883e71070d107bf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.4+1"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2+2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
validators:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: validators
|
||||
sha256: "884515951f831a9c669a41ed6c4d3c61c2a0e8ec6bca761a4480b28e99cecf5d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
windows_iap:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0+3"
|
||||
sdks:
|
||||
dart: ">=3.8.0-0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
85
windows_iap/example/pubspec.yaml
Normal file
85
windows_iap/example/pubspec.yaml
Normal file
@@ -0,0 +1,85 @@
|
||||
name: windows_iap_example
|
||||
description: Demonstrates how to use the windows_iap plugin.
|
||||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.0 <3.0.0"
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||
# dependencies can be manually updated by changing the version numbers below to
|
||||
# the latest version available on pub.dev. To see which dependencies have newer
|
||||
# versions available, run `flutter pub outdated`.
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
windows_iap:
|
||||
# When depending on this package from a real application you should use:
|
||||
# windows_iap: x.y.z
|
||||
# See https://dart.dev/tools/pub/dependencies#version-constraints
|
||||
# The example app is bundled with the plugin so we use a path dependency on
|
||||
# the parent directory to use the current plugin's version.
|
||||
path: ../
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: 1.0.5
|
||||
andesgroup_common: ^1.0.19
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
# The "flutter_lints" package below contains a set of recommended lints to
|
||||
# encourage good coding practices. The lint set provided by the package is
|
||||
# activated in the `analysis_options.yaml` file located at the root of your
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: 2.0.1
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
16
windows_iap/example/test/widget_test.dart
Normal file
16
windows_iap/example/test/widget_test.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility in the flutter_test package. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Verify Platform version', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
|
||||
// Verify that platform version is retrieved.
|
||||
});
|
||||
}
|
||||
17
windows_iap/example/windows/.gitignore
vendored
Normal file
17
windows_iap/example/windows/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
flutter/ephemeral/
|
||||
|
||||
# Visual Studio user-specific files.
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Visual Studio build-related files.
|
||||
x64/
|
||||
x86/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
101
windows_iap/example/windows/CMakeLists.txt
Normal file
101
windows_iap/example/windows/CMakeLists.txt
Normal file
@@ -0,0 +1,101 @@
|
||||
# Project-level configuration.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(windows_iap_example LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "windows_iap_example")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
|
||||
# Define build configuration option.
|
||||
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(IS_MULTICONFIG)
|
||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
|
||||
CACHE STRING "" FORCE)
|
||||
else()
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||
STRING "Flutter build mode" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
"Debug" "Profile" "Release")
|
||||
endif()
|
||||
endif()
|
||||
# Define settings for the Profile build mode.
|
||||
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
# Use Unicode for all projects.
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
|
||||
# Compilation settings that should be applied to most targets.
|
||||
#
|
||||
# Be cautious about adding new options here, as plugins use this function by
|
||||
# default. In most cases, you should add new options to specific targets instead
|
||||
# of modifying this function.
|
||||
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
||||
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
|
||||
target_compile_options(${TARGET} PRIVATE /EHsc)
|
||||
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
|
||||
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
|
||||
endfunction()
|
||||
|
||||
# Flutter library and tool build rules.
|
||||
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
|
||||
# Application build; see runner/CMakeLists.txt.
|
||||
add_subdirectory("runner")
|
||||
|
||||
# Generated plugin build rules, which manage building the plugins and adding
|
||||
# them to the application.
|
||||
include(flutter/generated_plugins.cmake)
|
||||
|
||||
|
||||
# === Installation ===
|
||||
# Support files are copied into place next to the executable, so that it can
|
||||
# run in place. This is done instead of making a separate bundle (as on Linux)
|
||||
# so that building and running from within Visual Studio will work.
|
||||
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
|
||||
# Make the "install" step default, as it's required to run.
|
||||
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||
endif()
|
||||
|
||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
if(PLUGIN_BUNDLED_LIBRARIES)
|
||||
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
|
||||
# Install the AOT library on non-Debug builds only.
|
||||
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
CONFIGURATIONS Profile;Release
|
||||
COMPONENT Runtime)
|
||||
104
windows_iap/example/windows/flutter/CMakeLists.txt
Normal file
104
windows_iap/example/windows/flutter/CMakeLists.txt
Normal file
@@ -0,0 +1,104 @@
|
||||
# This file controls Flutter-level build steps. It should not be edited.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||
|
||||
# Configuration provided via flutter tool.
|
||||
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||
|
||||
# TODO: Move the rest of this into files in ephemeral. See
|
||||
# https://github.com/flutter/flutter/issues/57146.
|
||||
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
||||
|
||||
# === Flutter Library ===
|
||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
|
||||
|
||||
# Published to parent scope for install step.
|
||||
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
|
||||
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
|
||||
|
||||
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||
"flutter_export.h"
|
||||
"flutter_windows.h"
|
||||
"flutter_messenger.h"
|
||||
"flutter_plugin_registrar.h"
|
||||
"flutter_texture_registrar.h"
|
||||
)
|
||||
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
|
||||
add_library(flutter INTERFACE)
|
||||
target_include_directories(flutter INTERFACE
|
||||
"${EPHEMERAL_DIR}"
|
||||
)
|
||||
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
|
||||
add_dependencies(flutter flutter_assemble)
|
||||
|
||||
# === Wrapper ===
|
||||
list(APPEND CPP_WRAPPER_SOURCES_CORE
|
||||
"core_implementations.cc"
|
||||
"standard_codec.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
|
||||
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
|
||||
"plugin_registrar.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
|
||||
list(APPEND CPP_WRAPPER_SOURCES_APP
|
||||
"flutter_engine.cc"
|
||||
"flutter_view_controller.cc"
|
||||
)
|
||||
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
|
||||
|
||||
# Wrapper sources needed for a plugin.
|
||||
add_library(flutter_wrapper_plugin STATIC
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
)
|
||||
apply_standard_settings(flutter_wrapper_plugin)
|
||||
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON)
|
||||
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
|
||||
target_include_directories(flutter_wrapper_plugin PUBLIC
|
||||
"${WRAPPER_ROOT}/include"
|
||||
)
|
||||
add_dependencies(flutter_wrapper_plugin flutter_assemble)
|
||||
|
||||
# Wrapper sources needed for the runner.
|
||||
add_library(flutter_wrapper_app STATIC
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
)
|
||||
apply_standard_settings(flutter_wrapper_app)
|
||||
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
|
||||
target_include_directories(flutter_wrapper_app PUBLIC
|
||||
"${WRAPPER_ROOT}/include"
|
||||
)
|
||||
add_dependencies(flutter_wrapper_app flutter_assemble)
|
||||
|
||||
# === Flutter tool backend ===
|
||||
# _phony_ is a non-existent file to force this command to run every time,
|
||||
# since currently there's no way to get a full input/output list from the
|
||||
# flutter tool.
|
||||
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
|
||||
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
|
||||
add_custom_command(
|
||||
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
${PHONY_OUTPUT}
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
${FLUTTER_TOOL_ENVIRONMENT}
|
||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
||||
windows-x64 $<CONFIG>
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(flutter_assemble DEPENDS
|
||||
"${FLUTTER_LIBRARY}"
|
||||
${FLUTTER_LIBRARY_HEADERS}
|
||||
${CPP_WRAPPER_SOURCES_CORE}
|
||||
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||
${CPP_WRAPPER_SOURCES_APP}
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <windows_iap/windows_iap_plugin_c_api.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
WindowsIapPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowsIapPluginCApi"));
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter/plugin_registry.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
||||
24
windows_iap/example/windows/flutter/generated_plugins.cmake
Normal file
24
windows_iap/example/windows/flutter/generated_plugins.cmake
Normal file
@@ -0,0 +1,24 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
windows_iap
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
32
windows_iap/example/windows/runner/CMakeLists.txt
Normal file
32
windows_iap/example/windows/runner/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(runner LANGUAGES CXX)
|
||||
|
||||
# Define the application target. To change its name, change BINARY_NAME in the
|
||||
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
|
||||
# work.
|
||||
#
|
||||
# Any new source files that you add to the application should be added here.
|
||||
add_executable(${BINARY_NAME} WIN32
|
||||
"flutter_window.cpp"
|
||||
"main.cpp"
|
||||
"utils.cpp"
|
||||
"win32_window.cpp"
|
||||
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||
"Runner.rc"
|
||||
"runner.exe.manifest"
|
||||
)
|
||||
|
||||
# Apply the standard set of build settings. This can be removed for applications
|
||||
# that need different build settings.
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
|
||||
# Disable Windows macros that collide with C++ standard library functions.
|
||||
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
|
||||
|
||||
# Add dependency libraries and include directories. Add any application-specific
|
||||
# dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||
121
windows_iap/example/windows/runner/Runner.rc
Normal file
121
windows_iap/example/windows/runner/Runner.rc
Normal file
@@ -0,0 +1,121 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#pragma code_page(65001)
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_APP_ICON ICON "resources\\app_icon.ico"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
#ifdef FLUTTER_BUILD_NUMBER
|
||||
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
|
||||
#else
|
||||
#define VERSION_AS_NUMBER 1,0,0
|
||||
#endif
|
||||
|
||||
#ifdef FLUTTER_BUILD_NAME
|
||||
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
|
||||
#else
|
||||
#define VERSION_AS_STRING "1.0.0"
|
||||
#endif
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VERSION_AS_NUMBER
|
||||
PRODUCTVERSION VERSION_AS_NUMBER
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904e4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "com.andesgroup" "\0"
|
||||
VALUE "FileDescription", "windows_iap_example" "\0"
|
||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||
VALUE "InternalName", "windows_iap_example" "\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2022 com.andesgroup. All rights reserved." "\0"
|
||||
VALUE "OriginalFilename", "windows_iap_example.exe" "\0"
|
||||
VALUE "ProductName", "windows_iap_example" "\0"
|
||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
61
windows_iap/example/windows/runner/flutter_window.cpp
Normal file
61
windows_iap/example/windows/runner/flutter_window.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "flutter_window.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
|
||||
: project_(project) {}
|
||||
|
||||
FlutterWindow::~FlutterWindow() {}
|
||||
|
||||
bool FlutterWindow::OnCreate() {
|
||||
if (!Win32Window::OnCreate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT frame = GetClientArea();
|
||||
|
||||
// The size here must match the window dimensions to avoid unnecessary surface
|
||||
// creation / destruction in the startup path.
|
||||
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
|
||||
frame.right - frame.left, frame.bottom - frame.top, project_);
|
||||
// Ensure that basic setup of the controller was successful.
|
||||
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
|
||||
return false;
|
||||
}
|
||||
RegisterPlugins(flutter_controller_->engine());
|
||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlutterWindow::OnDestroy() {
|
||||
if (flutter_controller_) {
|
||||
flutter_controller_ = nullptr;
|
||||
}
|
||||
|
||||
Win32Window::OnDestroy();
|
||||
}
|
||||
|
||||
LRESULT
|
||||
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
// Give Flutter, including plugins, an opportunity to handle window messages.
|
||||
if (flutter_controller_) {
|
||||
std::optional<LRESULT> result =
|
||||
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
|
||||
lparam);
|
||||
if (result) {
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case WM_FONTCHANGE:
|
||||
flutter_controller_->engine()->ReloadSystemFonts();
|
||||
break;
|
||||
}
|
||||
|
||||
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
|
||||
}
|
||||
33
windows_iap/example/windows/runner/flutter_window.h
Normal file
33
windows_iap/example/windows/runner/flutter_window.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef RUNNER_FLUTTER_WINDOW_H_
|
||||
#define RUNNER_FLUTTER_WINDOW_H_
|
||||
|
||||
#include <flutter/dart_project.h>
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "win32_window.h"
|
||||
|
||||
// A window that does nothing but host a Flutter view.
|
||||
class FlutterWindow : public Win32Window {
|
||||
public:
|
||||
// Creates a new FlutterWindow hosting a Flutter view running |project|.
|
||||
explicit FlutterWindow(const flutter::DartProject& project);
|
||||
virtual ~FlutterWindow();
|
||||
|
||||
protected:
|
||||
// Win32Window:
|
||||
bool OnCreate() override;
|
||||
void OnDestroy() override;
|
||||
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept override;
|
||||
|
||||
private:
|
||||
// The project to run.
|
||||
flutter::DartProject project_;
|
||||
|
||||
// The Flutter instance hosted by this window.
|
||||
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
|
||||
};
|
||||
|
||||
#endif // RUNNER_FLUTTER_WINDOW_H_
|
||||
43
windows_iap/example/windows/runner/main.cpp
Normal file
43
windows_iap/example/windows/runner/main.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <flutter/dart_project.h>
|
||||
#include <flutter/flutter_view_controller.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "flutter_window.h"
|
||||
#include "utils.h"
|
||||
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||
// Attach to console when present (e.g., 'flutter run') or create a
|
||||
// new console when running with a debugger.
|
||||
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
|
||||
CreateAndAttachConsole();
|
||||
}
|
||||
|
||||
// Initialize COM, so that it is available for use in the library and/or
|
||||
// plugins.
|
||||
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
|
||||
flutter::DartProject project(L"data");
|
||||
|
||||
std::vector<std::string> command_line_arguments =
|
||||
GetCommandLineArguments();
|
||||
|
||||
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
|
||||
|
||||
FlutterWindow window(project);
|
||||
Win32Window::Point origin(10, 10);
|
||||
Win32Window::Size size(1280, 720);
|
||||
if (!window.CreateAndShow(L"windows_iap_example", origin, size)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
window.SetQuitOnClose(true);
|
||||
|
||||
::MSG msg;
|
||||
while (::GetMessage(&msg, nullptr, 0, 0)) {
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
::CoUninitialize();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
16
windows_iap/example/windows/runner/resource.h
Normal file
16
windows_iap/example/windows/runner/resource.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by Runner.rc
|
||||
//
|
||||
#define IDI_APP_ICON 101
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
BIN
windows_iap/example/windows/runner/resources/app_icon.ico
Normal file
BIN
windows_iap/example/windows/runner/resources/app_icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
20
windows_iap/example/windows/runner/runner.exe.manifest
Normal file
20
windows_iap/example/windows/runner/runner.exe.manifest
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
64
windows_iap/example/windows/runner/utils.cpp
Normal file
64
windows_iap/example/windows/runner/utils.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <flutter_windows.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void CreateAndAttachConsole() {
|
||||
if (::AllocConsole()) {
|
||||
FILE *unused;
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
|
||||
_dup2(_fileno(stdout), 1);
|
||||
}
|
||||
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
|
||||
_dup2(_fileno(stdout), 2);
|
||||
}
|
||||
std::ios::sync_with_stdio();
|
||||
FlutterDesktopResyncOutputStreams();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> GetCommandLineArguments() {
|
||||
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
|
||||
int argc;
|
||||
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
|
||||
if (argv == nullptr) {
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<std::string> command_line_arguments;
|
||||
|
||||
// Skip the first argument as it's the binary name.
|
||||
for (int i = 1; i < argc; i++) {
|
||||
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
|
||||
}
|
||||
|
||||
::LocalFree(argv);
|
||||
|
||||
return command_line_arguments;
|
||||
}
|
||||
|
||||
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
|
||||
if (utf16_string == nullptr) {
|
||||
return std::string();
|
||||
}
|
||||
int target_length = ::WideCharToMultiByte(
|
||||
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||
-1, nullptr, 0, nullptr, nullptr);
|
||||
std::string utf8_string;
|
||||
if (target_length == 0 || target_length > utf8_string.max_size()) {
|
||||
return utf8_string;
|
||||
}
|
||||
utf8_string.resize(target_length);
|
||||
int converted_length = ::WideCharToMultiByte(
|
||||
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||
-1, utf8_string.data(),
|
||||
target_length, nullptr, nullptr);
|
||||
if (converted_length == 0) {
|
||||
return std::string();
|
||||
}
|
||||
return utf8_string;
|
||||
}
|
||||
19
windows_iap/example/windows/runner/utils.h
Normal file
19
windows_iap/example/windows/runner/utils.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef RUNNER_UTILS_H_
|
||||
#define RUNNER_UTILS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Creates a console for the process, and redirects stdout and stderr to
|
||||
// it for both the runner and the Flutter library.
|
||||
void CreateAndAttachConsole();
|
||||
|
||||
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
|
||||
// encoded in UTF-8. Returns an empty std::string on failure.
|
||||
std::string Utf8FromUtf16(const wchar_t* utf16_string);
|
||||
|
||||
// Gets the command line arguments passed in as a std::vector<std::string>,
|
||||
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
|
||||
std::vector<std::string> GetCommandLineArguments();
|
||||
|
||||
#endif // RUNNER_UTILS_H_
|
||||
245
windows_iap/example/windows/runner/win32_window.cpp
Normal file
245
windows_iap/example/windows/runner/win32_window.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "win32_window.h"
|
||||
|
||||
#include <flutter_windows.h>
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||
|
||||
// The number of Win32Window objects that currently exist.
|
||||
static int g_active_window_count = 0;
|
||||
|
||||
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
|
||||
|
||||
// Scale helper to convert logical scaler values to physical using passed in
|
||||
// scale factor
|
||||
int Scale(int source, double scale_factor) {
|
||||
return static_cast<int>(source * scale_factor);
|
||||
}
|
||||
|
||||
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
|
||||
// This API is only needed for PerMonitor V1 awareness mode.
|
||||
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
|
||||
HMODULE user32_module = LoadLibraryA("User32.dll");
|
||||
if (!user32_module) {
|
||||
return;
|
||||
}
|
||||
auto enable_non_client_dpi_scaling =
|
||||
reinterpret_cast<EnableNonClientDpiScaling*>(
|
||||
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
||||
if (enable_non_client_dpi_scaling != nullptr) {
|
||||
enable_non_client_dpi_scaling(hwnd);
|
||||
FreeLibrary(user32_module);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Manages the Win32Window's window class registration.
|
||||
class WindowClassRegistrar {
|
||||
public:
|
||||
~WindowClassRegistrar() = default;
|
||||
|
||||
// Returns the singleton registar instance.
|
||||
static WindowClassRegistrar* GetInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new WindowClassRegistrar();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
// Returns the name of the window class, registering the class if it hasn't
|
||||
// previously been registered.
|
||||
const wchar_t* GetWindowClass();
|
||||
|
||||
// Unregisters the window class. Should only be called if there are no
|
||||
// instances of the window.
|
||||
void UnregisterWindowClass();
|
||||
|
||||
private:
|
||||
WindowClassRegistrar() = default;
|
||||
|
||||
static WindowClassRegistrar* instance_;
|
||||
|
||||
bool class_registered_ = false;
|
||||
};
|
||||
|
||||
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
|
||||
|
||||
const wchar_t* WindowClassRegistrar::GetWindowClass() {
|
||||
if (!class_registered_) {
|
||||
WNDCLASS window_class{};
|
||||
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
window_class.lpszClassName = kWindowClassName;
|
||||
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||
window_class.cbClsExtra = 0;
|
||||
window_class.cbWndExtra = 0;
|
||||
window_class.hInstance = GetModuleHandle(nullptr);
|
||||
window_class.hIcon =
|
||||
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
|
||||
window_class.hbrBackground = 0;
|
||||
window_class.lpszMenuName = nullptr;
|
||||
window_class.lpfnWndProc = Win32Window::WndProc;
|
||||
RegisterClass(&window_class);
|
||||
class_registered_ = true;
|
||||
}
|
||||
return kWindowClassName;
|
||||
}
|
||||
|
||||
void WindowClassRegistrar::UnregisterWindowClass() {
|
||||
UnregisterClass(kWindowClassName, nullptr);
|
||||
class_registered_ = false;
|
||||
}
|
||||
|
||||
Win32Window::Win32Window() {
|
||||
++g_active_window_count;
|
||||
}
|
||||
|
||||
Win32Window::~Win32Window() {
|
||||
--g_active_window_count;
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size) {
|
||||
Destroy();
|
||||
|
||||
const wchar_t* window_class =
|
||||
WindowClassRegistrar::GetInstance()->GetWindowClass();
|
||||
|
||||
const POINT target_point = {static_cast<LONG>(origin.x),
|
||||
static_cast<LONG>(origin.y)};
|
||||
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
|
||||
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
|
||||
double scale_factor = dpi / 96.0;
|
||||
|
||||
HWND window = CreateWindow(
|
||||
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
||||
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return OnCreate();
|
||||
}
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
if (message == WM_NCCREATE) {
|
||||
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||||
SetWindowLongPtr(window, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
|
||||
|
||||
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
|
||||
EnableFullDpiSupportIfAvailable(window);
|
||||
that->window_handle_ = window;
|
||||
} else if (Win32Window* that = GetThisFromHandle(window)) {
|
||||
return that->MessageHandler(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
LRESULT
|
||||
Win32Window::MessageHandler(HWND hwnd,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
switch (message) {
|
||||
case WM_DESTROY:
|
||||
window_handle_ = nullptr;
|
||||
Destroy();
|
||||
if (quit_on_close_) {
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_DPICHANGED: {
|
||||
auto newRectSize = reinterpret_cast<RECT*>(lparam);
|
||||
LONG newWidth = newRectSize->right - newRectSize->left;
|
||||
LONG newHeight = newRectSize->bottom - newRectSize->top;
|
||||
|
||||
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
|
||||
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
case WM_SIZE: {
|
||||
RECT rect = GetClientArea();
|
||||
if (child_content_ != nullptr) {
|
||||
// Size and position the child window.
|
||||
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
|
||||
rect.bottom - rect.top, TRUE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_ACTIVATE:
|
||||
if (child_content_ != nullptr) {
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(window_handle_, message, wparam, lparam);
|
||||
}
|
||||
|
||||
void Win32Window::Destroy() {
|
||||
OnDestroy();
|
||||
|
||||
if (window_handle_) {
|
||||
DestroyWindow(window_handle_);
|
||||
window_handle_ = nullptr;
|
||||
}
|
||||
if (g_active_window_count == 0) {
|
||||
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
|
||||
}
|
||||
}
|
||||
|
||||
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
|
||||
return reinterpret_cast<Win32Window*>(
|
||||
GetWindowLongPtr(window, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
void Win32Window::SetChildContent(HWND content) {
|
||||
child_content_ = content;
|
||||
SetParent(content, window_handle_);
|
||||
RECT frame = GetClientArea();
|
||||
|
||||
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
|
||||
frame.bottom - frame.top, true);
|
||||
|
||||
SetFocus(child_content_);
|
||||
}
|
||||
|
||||
RECT Win32Window::GetClientArea() {
|
||||
RECT frame;
|
||||
GetClientRect(window_handle_, &frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
HWND Win32Window::GetHandle() {
|
||||
return window_handle_;
|
||||
}
|
||||
|
||||
void Win32Window::SetQuitOnClose(bool quit_on_close) {
|
||||
quit_on_close_ = quit_on_close;
|
||||
}
|
||||
|
||||
bool Win32Window::OnCreate() {
|
||||
// No-op; provided for subclasses.
|
||||
return true;
|
||||
}
|
||||
|
||||
void Win32Window::OnDestroy() {
|
||||
// No-op; provided for subclasses.
|
||||
}
|
||||
98
windows_iap/example/windows/runner/win32_window.h
Normal file
98
windows_iap/example/windows/runner/win32_window.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#ifndef RUNNER_WIN32_WINDOW_H_
|
||||
#define RUNNER_WIN32_WINDOW_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
|
||||
// inherited from by classes that wish to specialize with custom
|
||||
// rendering and input handling
|
||||
class Win32Window {
|
||||
public:
|
||||
struct Point {
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
|
||||
};
|
||||
|
||||
struct Size {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
Size(unsigned int width, unsigned int height)
|
||||
: width(width), height(height) {}
|
||||
};
|
||||
|
||||
Win32Window();
|
||||
virtual ~Win32Window();
|
||||
|
||||
// Creates and shows a win32 window with |title| and position and size using
|
||||
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||
// consistent size to will treat the width height passed in to this function
|
||||
// as logical pixels and scale to appropriate for the default monitor. Returns
|
||||
// true if the window was created successfully.
|
||||
bool CreateAndShow(const std::wstring& title,
|
||||
const Point& origin,
|
||||
const Size& size);
|
||||
|
||||
// Release OS resources associated with window.
|
||||
void Destroy();
|
||||
|
||||
// Inserts |content| into the window tree.
|
||||
void SetChildContent(HWND content);
|
||||
|
||||
// Returns the backing Window handle to enable clients to set icon and other
|
||||
// window properties. Returns nullptr if the window has been destroyed.
|
||||
HWND GetHandle();
|
||||
|
||||
// If true, closing this window will quit the application.
|
||||
void SetQuitOnClose(bool quit_on_close);
|
||||
|
||||
// Return a RECT representing the bounds of the current client area.
|
||||
RECT GetClientArea();
|
||||
|
||||
protected:
|
||||
// Processes and route salient window messages for mouse handling,
|
||||
// size change and DPI. Delegates handling of these to member overloads that
|
||||
// inheriting classes can handle.
|
||||
virtual LRESULT MessageHandler(HWND window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// Called when CreateAndShow is called, allowing subclass window-related
|
||||
// setup. Subclasses should return false if setup fails.
|
||||
virtual bool OnCreate();
|
||||
|
||||
// Called when Destroy is called.
|
||||
virtual void OnDestroy();
|
||||
|
||||
private:
|
||||
friend class WindowClassRegistrar;
|
||||
|
||||
// OS callback called by message pump. Handles the WM_NCCREATE message which
|
||||
// is passed when the non-client area is being created and enables automatic
|
||||
// non-client DPI scaling so that the non-client area automatically
|
||||
// responsponds to changes in DPI. All other messages are handled by
|
||||
// MessageHandler.
|
||||
static LRESULT CALLBACK WndProc(HWND const window,
|
||||
UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept;
|
||||
|
||||
// Retrieves a class instance pointer for |window|
|
||||
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||
|
||||
bool quit_on_close_ = false;
|
||||
|
||||
// window handle for top level window.
|
||||
HWND window_handle_ = nullptr;
|
||||
|
||||
// window handle for hosted content.
|
||||
HWND child_content_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // RUNNER_WIN32_WINDOW_H_
|
||||
41
windows_iap/lib/models/product.dart
Normal file
41
windows_iap/lib/models/product.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
class Product {
|
||||
final String? title;
|
||||
final String? description;
|
||||
final String? price;
|
||||
final bool? inCollection;
|
||||
final String? productKind;
|
||||
final String? storeId;
|
||||
|
||||
const Product({
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.price,
|
||||
required this.inCollection,
|
||||
required this.productKind,
|
||||
required this.storeId,
|
||||
});
|
||||
|
||||
String? get formattedTitle {
|
||||
return "$title ($productKind) $price, InUserCollection: $inCollection";
|
||||
}
|
||||
|
||||
factory Product.fromJson(Map<String, dynamic> json) {
|
||||
return Product(
|
||||
title: json['title'] as String?,
|
||||
description: json['description'] as String?,
|
||||
price: json['price'] as String?,
|
||||
inCollection: json['inCollection'] as bool?,
|
||||
productKind: json['productKind'] as String?,
|
||||
storeId: json['storeId'] as String?);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'title': title,
|
||||
'price': price,
|
||||
'inCellection': inCollection,
|
||||
'productKind': productKind,
|
||||
'storeId': storeId,
|
||||
};
|
||||
}
|
||||
}
|
||||
48
windows_iap/lib/models/store_license.dart
Normal file
48
windows_iap/lib/models/store_license.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
class StoreLicense {
|
||||
final bool? isActive;
|
||||
final String? skuStoreId;
|
||||
final String? inAppOfferToken;
|
||||
final num? expirationDate;
|
||||
|
||||
const StoreLicense({
|
||||
this.isActive,
|
||||
this.skuStoreId,
|
||||
this.inAppOfferToken,
|
||||
this.expirationDate,
|
||||
});
|
||||
|
||||
/// get Expiration date in DateTime format.
|
||||
DateTime? getExpirationDate() {
|
||||
if (expirationDate == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var magic = num.tryParse(expirationDate.toString().substring(0, 14));
|
||||
if (magic == null) {
|
||||
return null;
|
||||
}
|
||||
// 11644499200000 is the count of millis from DateTime(1601,1,1) -> DateTime(1970,1,1)
|
||||
// more docs: https://docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/clock
|
||||
// https://docs.microsoft.com/en-us/uwp/api/windows.foundation.datetime?view=winrt-22621#remarks
|
||||
// https://docs.microsoft.com/en-us/uwp/api/windows.services.store.storelicense.expirationdate?view=winrt-22621
|
||||
magic = magic - 11644499200000;
|
||||
return DateTime.fromMillisecondsSinceEpoch(magic.toInt());
|
||||
}
|
||||
|
||||
factory StoreLicense.fromJson(Map<String, dynamic> json) {
|
||||
return StoreLicense(
|
||||
isActive: json['isActive'],
|
||||
skuStoreId: json['skuStoreId'],
|
||||
inAppOfferToken: json['inAppOfferToken'],
|
||||
expirationDate: json['expirationDate']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'isActive': isActive,
|
||||
'skuStoreId': skuStoreId,
|
||||
'inAppOfferToken': inAppOfferToken,
|
||||
'expirationDate': expirationDate,
|
||||
};
|
||||
}
|
||||
}
|
||||
6
windows_iap/lib/utils.dart
Normal file
6
windows_iap/lib/utils.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
List<T> parseListNotNull<T extends Object?>({
|
||||
required List<dynamic> json,
|
||||
required T Function(Map<String, dynamic> json) fromJson,
|
||||
}) {
|
||||
return (json).map((e) => fromJson(e as Map<String, dynamic>)).toList();
|
||||
}
|
||||
67
windows_iap/lib/windows_iap.dart
Normal file
67
windows_iap/lib/windows_iap.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
library windows_iap;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:windows_iap/models/product.dart';
|
||||
|
||||
import 'models/store_license.dart';
|
||||
import 'windows_iap_platform_interface.dart';
|
||||
|
||||
|
||||
enum StorePurchaseStatus {
|
||||
succeeded,
|
||||
alreadyPurchased,
|
||||
notPurchased,
|
||||
networkError,
|
||||
serverError,
|
||||
}
|
||||
|
||||
class WindowsIap {
|
||||
Future<StorePurchaseStatus?> makePurchase(String storeId) {
|
||||
return WindowsIapPlatform.instance.makePurchase(storeId);
|
||||
}
|
||||
|
||||
/// throw PlatformException if error
|
||||
Future<List<Product>> getProducts() {
|
||||
if (Platform.isMacOS) {
|
||||
return Future.delayed(const Duration(seconds: 2), () {
|
||||
throw PlatformException(code: '123123123', message: 'Products can not loaded now.');
|
||||
});
|
||||
}
|
||||
return WindowsIapPlatform.instance.getProducts();
|
||||
}
|
||||
|
||||
/// Check when user has current valid purchase
|
||||
///
|
||||
/// - Add-On type: Subscription, Durable
|
||||
///
|
||||
/// - Always return false if AppLicense has IsActive status = false.
|
||||
///
|
||||
/// - if storeId is Not Empty:
|
||||
///
|
||||
/// -- it will return true if Product(storeId) has IsActive status = true.
|
||||
///
|
||||
/// -- return false if not.
|
||||
///
|
||||
/// - if storeId is Empty:
|
||||
///
|
||||
/// -- it will return true if any Add-On have IsActive status = true.
|
||||
///
|
||||
/// -- return false if all Add-On have IsActive status = false.
|
||||
Future<bool> checkPurchase({String storeId = ''}) {
|
||||
if (Platform.isMacOS) {
|
||||
return Future.value(false);
|
||||
}
|
||||
return WindowsIapPlatform.instance.checkPurchase(storeId: storeId);
|
||||
}
|
||||
|
||||
/// return the map of StoreLicense
|
||||
///
|
||||
/// A map of key and value pairs, where each key is the Store ID of an add-on SKU from the
|
||||
/// Microsoft Store catalog and each value is a StoreLicense object that contains license
|
||||
/// info for the add-on.
|
||||
Future<Map<String, StoreLicense>> getAddonLicenses() {
|
||||
return WindowsIapPlatform.instance.getAddonLicenses();
|
||||
}
|
||||
}
|
||||
114
windows_iap/lib/windows_iap_method_channel.dart
Normal file
114
windows_iap/lib/windows_iap_method_channel.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'models/product.dart';
|
||||
import 'models/store_license.dart';
|
||||
import 'utils.dart';
|
||||
import 'windows_iap.dart';
|
||||
import 'windows_iap_platform_interface.dart';
|
||||
|
||||
/// A [Map] between whitespace characters and their escape sequences.
|
||||
const _escapeMap = {
|
||||
'\n': '',
|
||||
'\r': '',
|
||||
'\f': '',
|
||||
'\b': '',
|
||||
'\t': '',
|
||||
'\v': '',
|
||||
'\x7F': '', // delete
|
||||
};
|
||||
|
||||
/// A [RegExp] that matches whitespace characters that should be escaped.
|
||||
var _escapeRegExp = RegExp(
|
||||
'[\\x00-\\x07\\x0E-\\x1F${_escapeMap.keys.map(_getHexLiteral).join()}]');
|
||||
|
||||
/// Returns [str] with all whitespace characters represented as their escape
|
||||
/// sequences.
|
||||
///
|
||||
/// Backslash characters are escaped as `\\`
|
||||
String escape(String str) {
|
||||
str = str.replaceAll('\\', r'\\');
|
||||
return str.replaceAllMapped(_escapeRegExp, (match) {
|
||||
var mapped = _escapeMap[match[0]];
|
||||
if (mapped != null) return mapped;
|
||||
return _getHexLiteral(match[0]!);
|
||||
});
|
||||
}
|
||||
|
||||
/// Given single-character string, return the hex-escaped equivalent.
|
||||
String _getHexLiteral(String input) {
|
||||
var rune = input.runes.single;
|
||||
return r'\x' + rune.toRadixString(16).toUpperCase().padLeft(2, '0');
|
||||
}
|
||||
|
||||
/// An implementation of [WindowsIapPlatform] that uses method channels.
|
||||
class MethodChannelWindowsIap extends WindowsIapPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('windows_iap');
|
||||
|
||||
@override
|
||||
Future<StorePurchaseStatus?> makePurchase(String storeId) async {
|
||||
final result = await methodChannel
|
||||
.invokeMethod<int>('makePurchase', {'storeId': storeId});
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
switch (result) {
|
||||
case 0:
|
||||
return StorePurchaseStatus.succeeded;
|
||||
case 1:
|
||||
return StorePurchaseStatus.alreadyPurchased;
|
||||
case 2:
|
||||
return StorePurchaseStatus.notPurchased;
|
||||
case 3:
|
||||
return StorePurchaseStatus.networkError;
|
||||
case 4:
|
||||
return StorePurchaseStatus.serverError;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<List<Product>> productsStream() {
|
||||
return const EventChannel('windows_iap_event_products')
|
||||
.receiveBroadcastStream()
|
||||
.map((event) {
|
||||
if (event is String) {
|
||||
return parseListNotNull(
|
||||
json: jsonDecode(escape(event)), fromJson: Product.fromJson);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Product>> getProducts() async {
|
||||
final result = await methodChannel.invokeMethod<String>('getProducts');
|
||||
if (result == null) {
|
||||
return [];
|
||||
}
|
||||
return parseListNotNull(
|
||||
json: jsonDecode(escape(result)), fromJson: Product.fromJson);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> checkPurchase({required String storeId}) async {
|
||||
final result = await methodChannel
|
||||
.invokeMethod<bool>('checkPurchase', {'storeId': storeId});
|
||||
return result ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, StoreLicense>> getAddonLicenses() async {
|
||||
final result = await methodChannel.invokeMethod<Map>('getAddonLicenses');
|
||||
if (result == null) {
|
||||
return {};
|
||||
}
|
||||
return result.map((key, value) =>
|
||||
MapEntry(key.toString(), StoreLicense.fromJson(jsonDecode(value))));
|
||||
}
|
||||
}
|
||||
44
windows_iap/lib/windows_iap_platform_interface.dart
Normal file
44
windows_iap/lib/windows_iap_platform_interface.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
import 'package:windows_iap/models/product.dart';
|
||||
import 'package:windows_iap/models/store_license.dart';
|
||||
|
||||
import 'windows_iap.dart';
|
||||
import 'windows_iap_method_channel.dart';
|
||||
|
||||
abstract class WindowsIapPlatform extends PlatformInterface {
|
||||
/// Constructs a WindowsIapPlatform.
|
||||
WindowsIapPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static WindowsIapPlatform _instance = MethodChannelWindowsIap();
|
||||
|
||||
/// The default instance of [WindowsIapPlatform] to use.
|
||||
///
|
||||
/// Defaults to [MethodChannelWindowsIap].
|
||||
static WindowsIapPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [WindowsIapPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(WindowsIapPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<StorePurchaseStatus?> makePurchase(String storeId) {
|
||||
throw UnimplementedError('makePurchase() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<List<Product>> getProducts() {
|
||||
throw UnimplementedError('getProducts() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<bool> checkPurchase({required String storeId}) {
|
||||
throw UnimplementedError('checkPurchase() has not been implemented.');
|
||||
}
|
||||
|
||||
Future<Map<String, StoreLicense>> getAddonLicenses() {
|
||||
throw UnimplementedError('getAddonLicenses() has not been implemented.');
|
||||
}
|
||||
}
|
||||
24
windows_iap/pubspec.yaml
Normal file
24
windows_iap/pubspec.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
name: windows_iap
|
||||
description: A new Flutter project.
|
||||
version: 0.0.1
|
||||
homepage: https://github.com/opmgit/windows_iap
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.0 <3.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
plugin_platform_interface: ^2.1.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter:
|
||||
|
||||
plugin:
|
||||
platforms:
|
||||
windows:
|
||||
pluginClass: WindowsIapPluginCApi
|
||||
24
windows_iap/test/windows_iap_method_channel_test.dart
Normal file
24
windows_iap/test/windows_iap_method_channel_test.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:windows_iap/windows_iap_method_channel.dart';
|
||||
|
||||
void main() {
|
||||
MethodChannelWindowsIap platform = MethodChannelWindowsIap();
|
||||
const MethodChannel channel = MethodChannel('windows_iap');
|
||||
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUp(() {
|
||||
channel.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||
return '42';
|
||||
});
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
channel.setMockMethodCallHandler(null);
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
// expect(await platform.getPlatformVersion(), '42');
|
||||
});
|
||||
}
|
||||
7
windows_iap/test/windows_iap_test.dart
Normal file
7
windows_iap/test/windows_iap_test.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('1=1', () async {
|
||||
expect(1, 1);
|
||||
});
|
||||
}
|
||||
1
windows_iap/watch.bat
Normal file
1
windows_iap/watch.bat
Normal file
@@ -0,0 +1 @@
|
||||
flutter pub run build_runner watch --delete-conflicting-outputs --use-polling-watcher
|
||||
17
windows_iap/windows/.gitignore
vendored
Normal file
17
windows_iap/windows/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
flutter/
|
||||
|
||||
# Visual Studio user-specific files.
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Visual Studio build-related files.
|
||||
x64/
|
||||
x86/
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
89
windows_iap/windows/CMakeLists.txt
Normal file
89
windows_iap/windows/CMakeLists.txt
Normal file
@@ -0,0 +1,89 @@
|
||||
# The Flutter tooling requires that developers have a version of Visual Studio
|
||||
# installed that includes CMake 3.14 or later. You should not increase this
|
||||
# version, as doing so will cause the plugin to fail to compile for some
|
||||
# customers of the plugin.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
# Project-level configuration.
|
||||
set(PROJECT_NAME "windows_iap")
|
||||
project(${PROJECT_NAME} LANGUAGES CXX)
|
||||
|
||||
include(FetchContent)
|
||||
set(CPPWINRT_VERSION "2.0.221121.5")
|
||||
|
||||
# This value is used when generating builds using this plugin, so it must
|
||||
# not be changed
|
||||
set(PLUGIN_NAME "windows_iap_plugin")
|
||||
|
||||
################ NuGet intall begin ################
|
||||
FetchContent_Declare(nuget
|
||||
URL "https://dist.nuget.org/win-x86-commandline/v6.4.0/nuget.exe"
|
||||
URL_HASH SHA256=26730829b240581a3e6a4e276b9ace088930032df0c680d5591beccf6452374e
|
||||
DOWNLOAD_NO_EXTRACT true
|
||||
)
|
||||
|
||||
find_program(NUGET nuget)
|
||||
if (NOT NUGET)
|
||||
message("Nuget.exe not found, trying to download or use cached version.")
|
||||
FetchContent_MakeAvailable(nuget)
|
||||
set(NUGET ${nuget_SOURCE_DIR}/nuget.exe)
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND
|
||||
${NUGET} install "Microsoft.Windows.CppWinRT" -Version ${CPPWINRT_VERSION} -OutputDirectory packages
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
RESULT_VARIABLE ret)
|
||||
if (NOT ret EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}")
|
||||
endif()
|
||||
################ NuGet install end ################
|
||||
|
||||
# Any new source files that you add to the plugin should be added here.
|
||||
list(APPEND PLUGIN_SOURCES
|
||||
"windows_iap_plugin.cpp"
|
||||
"windows_iap_plugin.h"
|
||||
)
|
||||
|
||||
# Define the plugin library target. Its name must not be changed (see comment
|
||||
# on PLUGIN_NAME above).
|
||||
add_library(${PLUGIN_NAME} SHARED
|
||||
"include/windows_iap/windows_iap_plugin_c_api.h"
|
||||
"windows_iap_plugin_c_api.cpp"
|
||||
${PLUGIN_SOURCES}
|
||||
)
|
||||
|
||||
# Apply a standard set of build settings that are configured in the
|
||||
# application-level CMakeLists.txt. This can be removed for plugins that want
|
||||
# full control over build settings.
|
||||
apply_standard_settings(${PLUGIN_NAME})
|
||||
|
||||
# Symbols are hidden by default to reduce the chance of accidental conflicts
|
||||
# between plugins. This should not be removed; any symbols that should be
|
||||
# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.
|
||||
set_target_properties(${PLUGIN_NAME} PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden)
|
||||
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
|
||||
|
||||
################ NuGet import begin ################
|
||||
set_target_properties(${PLUGIN_NAME} PROPERTIES VS_PROJECT_IMPORT
|
||||
${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/build/native/Microsoft.Windows.CppWinRT.props
|
||||
)
|
||||
|
||||
target_link_libraries(${PLUGIN_NAME} PRIVATE
|
||||
${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/build/native/Microsoft.Windows.CppWinRT.targets
|
||||
)
|
||||
################ NuGet import end ################
|
||||
|
||||
# Source include directories and library dependencies. Add any plugin-specific
|
||||
# dependencies here.
|
||||
target_include_directories(${PLUGIN_NAME} INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
|
||||
|
||||
# List of absolute paths to libraries that should be bundled with the plugin.
|
||||
# This list could contain prebuilt libraries, or libraries created by an
|
||||
# external build triggered from this build file.
|
||||
set(windows_iap_bundled_libraries
|
||||
""
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef FLUTTER_PLUGIN_WINDOWS_IAP_PLUGIN_C_API_H_
|
||||
#define FLUTTER_PLUGIN_WINDOWS_IAP_PLUGIN_C_API_H_
|
||||
|
||||
#include <flutter_plugin_registrar.h>
|
||||
|
||||
#ifdef FLUTTER_PLUGIN_IMPL
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
FLUTTER_PLUGIN_EXPORT void WindowsIapPluginCApiRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // FLUTTER_PLUGIN_WINDOWS_IAP_PLUGIN_C_API_H_
|
||||
280
windows_iap/windows/windows_iap_plugin.cpp
Normal file
280
windows_iap/windows/windows_iap_plugin.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
#include "windows_iap_plugin.h"
|
||||
|
||||
// This must be included before many other Windows headers.
|
||||
#include <windows.h>
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#pragma once
|
||||
#include <winrt/Windows.Services.Store.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <shobjidl.h>
|
||||
|
||||
#include <flutter/event_sink.h>
|
||||
#include <flutter/event_channel.h>
|
||||
#include <flutter/event_stream_handler.h>
|
||||
#include <flutter/event_stream_handler_functions.h>
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Windows::Services::Store;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
namespace foundation = Windows::Foundation;
|
||||
|
||||
namespace windows_iap {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////// BEGIN OF MY CODE //////////////////////////////////////////////////////////////
|
||||
flutter::PluginRegistrarWindows* _registrar;
|
||||
|
||||
HWND GetRootWindow(flutter::FlutterView* view) {
|
||||
return ::GetAncestor(view->GetNativeWindow(), GA_ROOT);
|
||||
}
|
||||
|
||||
StoreContext getStore() {
|
||||
StoreContext store = StoreContext::GetDefault();
|
||||
auto initWindow = store.try_as<IInitializeWithWindow>();
|
||||
if (initWindow != nullptr) {
|
||||
initWindow->Initialize(GetRootWindow(_registrar->GetView()));
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
std::wstring s2ws(const std::string& s)
|
||||
{
|
||||
int len;
|
||||
int slength = (int)s.length() + 1;
|
||||
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
|
||||
wchar_t* buf = new wchar_t[len];
|
||||
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
|
||||
std::wstring r(buf);
|
||||
delete[] buf;
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string debugString(std::vector<std::string> vt) {
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "( ";
|
||||
for (auto t : vt) {
|
||||
ss << t << ", ";
|
||||
}
|
||||
ss << " )\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string getExtendedErrorString(winrt::hresult error) {
|
||||
const HRESULT IAP_E_UNEXPECTED = 0x803f6107L;
|
||||
std::string message;
|
||||
if (error.value == IAP_E_UNEXPECTED) {
|
||||
message = "This Product has not been properly configured.";
|
||||
}
|
||||
else {
|
||||
message = "ExtendedError: " + std::to_string(error.value);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
foundation::IAsyncAction makePurchase(hstring storeId, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> resultCallback)
|
||||
{
|
||||
StorePurchaseResult result = co_await getStore().RequestPurchaseAsync(storeId);
|
||||
|
||||
if (result.ExtendedError().value != S_OK) {
|
||||
resultCallback->Error(std::to_string(result.ExtendedError().value), getExtendedErrorString(result.ExtendedError().value));
|
||||
co_return;
|
||||
}
|
||||
int32_t returnCode;
|
||||
switch (result.Status()) {
|
||||
case StorePurchaseStatus::AlreadyPurchased:
|
||||
returnCode = 1;
|
||||
break;
|
||||
|
||||
case StorePurchaseStatus::Succeeded:
|
||||
returnCode = 0;
|
||||
break;
|
||||
|
||||
case StorePurchaseStatus::NotPurchased:
|
||||
returnCode = 2;
|
||||
break;
|
||||
|
||||
case StorePurchaseStatus::NetworkError:
|
||||
returnCode = 3;
|
||||
break;
|
||||
|
||||
case StorePurchaseStatus::ServerError:
|
||||
returnCode = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
auto status = reinterpret_cast<int32_t*>(result.Status());
|
||||
resultCallback->Error(std::to_string(*status), "Product was not purchased due to an unknown error.");
|
||||
co_return;
|
||||
break;
|
||||
}
|
||||
|
||||
resultCallback->Success(flutter::EncodableValue(returnCode));
|
||||
}
|
||||
|
||||
std::string productsToString(std::vector<StoreProduct> products) {
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (int i = 0; i < products.size(); i++) {
|
||||
auto product = products.at(i);
|
||||
ss << "{";
|
||||
ss << "\"title\":\"" << to_string(product.Title()) << "\",";
|
||||
ss << "\"description\":\"" << to_string(product.Description()) << "\",";
|
||||
ss << "\"price\":\"" << to_string(product.Price().FormattedPrice()) << "\",";
|
||||
ss << "\"inCollection\":" << (product.IsInUserCollection() ? "true" : "false") << ",";
|
||||
ss << "\"productKind\":\"" << to_string(product.ProductKind()) << "\",";
|
||||
ss << "\"storeId\":\"" << to_string(product.StoreId()) << "\"";
|
||||
ss << "}";
|
||||
if (i != products.size() - 1) {
|
||||
ss << ",";
|
||||
}
|
||||
}
|
||||
ss << "]";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
foundation::IAsyncAction getProducts(std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> resultCallback) {
|
||||
auto result = co_await getStore().GetAssociatedStoreProductsAsync({ L"Consumable", L"Durable", L"UnmanagedConsumable" });
|
||||
if (result.ExtendedError().value != S_OK) {
|
||||
resultCallback->Error(std::to_string(result.ExtendedError().value), getExtendedErrorString(result.ExtendedError()));
|
||||
}
|
||||
else if (result.Products().Size() == 0) {
|
||||
resultCallback->Success(flutter::EncodableValue("[]"));
|
||||
|
||||
}
|
||||
else {
|
||||
std::vector<StoreProduct> products;
|
||||
for (IKeyValuePair<hstring, StoreProduct> addOn : result.Products())
|
||||
{
|
||||
StoreProduct product = addOn.Value();
|
||||
products.push_back(product);
|
||||
}
|
||||
std::string productsString = productsToString(products);
|
||||
resultCallback->Success(flutter::EncodableValue(productsString));
|
||||
}
|
||||
}
|
||||
|
||||
std::string getStoreLicenseString(StoreLicense license) {
|
||||
std::stringstream ss;
|
||||
ss << "{";
|
||||
ss << "\"isActive\":" << (license.IsActive() ? "true" : "false") << ",";
|
||||
ss << "\"skuStoreId\":\"" << to_string(license.SkuStoreId()) << "\",";
|
||||
ss << "\"inAppOfferToken\":\"" << to_string(license.InAppOfferToken()) << "\",";
|
||||
ss << "\"expirationDate\":" << license.ExpirationDate().time_since_epoch().count() << "";
|
||||
ss << "}";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
foundation::IAsyncAction getAddonLicenses(std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> resultCallback) {
|
||||
auto result = co_await getStore().GetAppLicenseAsync();
|
||||
auto addonLicenses = result.AddOnLicenses();
|
||||
|
||||
std::map<flutter::EncodableValue, flutter::EncodableValue> mapLicenses;
|
||||
|
||||
for (IKeyValuePair<hstring, StoreLicense> addonLicense : addonLicenses)
|
||||
{
|
||||
mapLicenses[flutter::EncodableValue(to_string(addonLicense.Key()))] = flutter::EncodableValue(getStoreLicenseString(addonLicense.Value()));
|
||||
}
|
||||
|
||||
resultCallback->Success(flutter::EncodableValue(mapLicenses));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// need to test in real app on store
|
||||
/// </summary>
|
||||
foundation::IAsyncAction checkPurchase(std::string storeId, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> resultCallback) {
|
||||
auto result = co_await getStore().GetAppLicenseAsync();
|
||||
|
||||
if (result.IsActive()) {
|
||||
|
||||
auto addonLicenses = result.AddOnLicenses();
|
||||
|
||||
for (IKeyValuePair<hstring, StoreLicense> addonLicense : addonLicenses)
|
||||
{
|
||||
StoreLicense license = addonLicense.Value();
|
||||
|
||||
if (storeId.compare("") == 0) {
|
||||
// Truong hop storeId empty => bat ky Add-on nao co IsActive = true deu return true
|
||||
if (license.IsActive()) {
|
||||
resultCallback->Success(flutter::EncodableValue(true));
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Truong hop storeId not empty => check key = storeId
|
||||
auto key = to_string(addonLicense.Key());
|
||||
if (key.compare(storeId) == 0) {
|
||||
resultCallback->Success(flutter::EncodableValue(license.IsActive()));
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// truong hop duyet het add-on license nhung vang khong tim thay IsActive = true thi return false
|
||||
resultCallback->Success(flutter::EncodableValue(false));
|
||||
}
|
||||
else {
|
||||
resultCallback->Success(flutter::EncodableValue(false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////// END OF MY CODE //////////////////////////////////////////////////////////////
|
||||
|
||||
// static
|
||||
void WindowsIapPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarWindows* registrar) {
|
||||
_registrar = registrar;
|
||||
|
||||
auto channel =
|
||||
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||||
registrar->messenger(), "windows_iap",
|
||||
&flutter::StandardMethodCodec::GetInstance());
|
||||
|
||||
auto plugin = std::make_unique<WindowsIapPlugin>();
|
||||
|
||||
channel->SetMethodCallHandler(
|
||||
[plugin_pointer = plugin.get()](const auto& call, auto result) {
|
||||
plugin_pointer->HandleMethodCall(call, std::move(result));
|
||||
});
|
||||
|
||||
registrar->AddPlugin(std::move(plugin));
|
||||
}
|
||||
|
||||
WindowsIapPlugin::WindowsIapPlugin() {}
|
||||
|
||||
WindowsIapPlugin::~WindowsIapPlugin() {}
|
||||
|
||||
void WindowsIapPlugin::HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
if (method_call.method_name().compare("makePurchase") == 0) {
|
||||
auto args = std::get<flutter::EncodableMap>(*method_call.arguments());
|
||||
auto storeId = std::get<std::string>(args[flutter::EncodableValue("storeId")]);
|
||||
makePurchase(to_hstring(storeId), std::move(result));
|
||||
}
|
||||
else if (method_call.method_name().compare("getProducts") == 0) {
|
||||
getProducts(std::move(result));
|
||||
}
|
||||
else if (method_call.method_name().compare("checkPurchase") == 0) {
|
||||
auto args = std::get<flutter::EncodableMap>(*method_call.arguments());
|
||||
auto storeId = std::get<std::string>(args[flutter::EncodableValue("storeId")]);
|
||||
checkPurchase(storeId, std::move(result));
|
||||
}
|
||||
else if (method_call.method_name().compare("getAddonLicenses") == 0) {
|
||||
getAddonLicenses(std::move(result));
|
||||
}
|
||||
else {
|
||||
result->NotImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace windows_iap
|
||||
32
windows_iap/windows/windows_iap_plugin.h
Normal file
32
windows_iap/windows/windows_iap_plugin.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef FLUTTER_PLUGIN_WINDOWS_IAP_PLUGIN_H_
|
||||
#define FLUTTER_PLUGIN_WINDOWS_IAP_PLUGIN_H_
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace windows_iap {
|
||||
|
||||
class WindowsIapPlugin : public flutter::Plugin {
|
||||
public:
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
|
||||
|
||||
WindowsIapPlugin();
|
||||
|
||||
virtual ~WindowsIapPlugin();
|
||||
|
||||
// Disallow copy and assign.
|
||||
WindowsIapPlugin(const WindowsIapPlugin&) = delete;
|
||||
WindowsIapPlugin& operator=(const WindowsIapPlugin&) = delete;
|
||||
|
||||
private:
|
||||
// Called when a method is called on this plugin's channel from Dart.
|
||||
void HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue> &method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
|
||||
};
|
||||
|
||||
} // namespace windows_iap
|
||||
|
||||
#endif // FLUTTER_PLUGIN_WINDOWS_IAP_PLUGIN_H_
|
||||
12
windows_iap/windows/windows_iap_plugin_c_api.cpp
Normal file
12
windows_iap/windows/windows_iap_plugin_c_api.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "include/windows_iap/windows_iap_plugin_c_api.h"
|
||||
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
|
||||
#include "windows_iap_plugin.h"
|
||||
|
||||
void WindowsIapPluginCApiRegisterWithRegistrar(
|
||||
FlutterDesktopPluginRegistrarRef registrar) {
|
||||
windows_iap::WindowsIapPlugin::RegisterWithRegistrar(
|
||||
flutter::PluginRegistrarManager::GetInstance()
|
||||
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
|
||||
}
|
||||
Reference in New Issue
Block a user