diff --git a/.github/labeler.yml b/.github/labeler.yml index dbb0844b6..c600ee321 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -41,6 +41,8 @@ - packages/tizen_app_manager/**/* 'p: tizen_audio_manager': - packages/tizen_audio_manager/**/* +'p: tizen_package_manager': + - packages/tizen_package_manager/**/* 'p: url_launcher': - packages/url_launcher/**/* 'p: video_player': diff --git a/.github/recipe.yaml b/.github/recipe.yaml index 4b70f85d4..ae13d4c7a 100644 --- a/.github/recipe.yaml +++ b/.github/recipe.yaml @@ -14,6 +14,7 @@ plugins: sqflite: ["wearable-5.5", "tv-6.0"] tizen_app_manager: ["wearable-5.5", "tv-6.0"] tizen_audio_manager: ["wearable-5.5", "tv-6.0"] + tizen_package_manager: ["wearable-5.5", "tv-6.0"] wakelock: ["wearable-5.5"] # Not supported by emulators. diff --git a/README.md b/README.md index e35d4d7cf..44a5772b3 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina | [**tizen_app_manager**](packages/tizen_app_manager) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_app_manager.svg)](https://pub.dev/packages/tizen_app_manager) | N/A | | [**tizen_audio_manager**](packages/tizen_audio_manager) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_audio_manager.svg)](https://pub.dev/packages/tizen_audio_manager) | N/A | | [**tizen_notification**](packages/tizen_notification) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_notification.svg)](https://pub.dev/packages/tizen_notification) | N/A | +| [**tizen_package_manager**](packages/tizen_package_manager) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_package_manager.svg)](https://pub.dev/packages/tizen_package_manager) | N/A | | [**url_launcher_tizen**](packages/url_launcher) | [url_launcher](https://github.com/flutter/plugins/tree/master/packages/url_launcher) (1st-party) | [![pub package](https://img.shields.io/pub/v/url_launcher_tizen.svg)](https://pub.dev/packages/url_launcher_tizen) | No | | [**video_player_tizen**](packages/video_player) | [video_player](https://github.com/flutter/plugins/tree/master/packages/video_player) (1st-party) | [![pub package](https://img.shields.io/pub/v/video_player_tizen.svg)](https://pub.dev/packages/video_player_tizen) | No | | [**wakelock_tizen**](packages/wakelock) | [wakelock](https://github.com/creativecreatorormaybenot/wakelock) (3rd-party) | [![pub package](https://img.shields.io/pub/v/wakelock_tizen.svg)](https://pub.dev/packages/wakelock_tizen) | No | @@ -70,6 +71,7 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina | [**tizen_app_manager**](packages/tizen_app_manager) | ✔️ | ✔️ | ✔️ | ✔️ | | [**tizen_audio_manager**](packages/tizen_audio_manager) | ✔️ | ✔️ | ✔️ | ✔️ | | [**tizen_notification**](packages/tizen_notification) | ❌ | ✔️ | ✔️ | ✔️ | API not supported | +| [**tizen_package_manager**](packages/tizen_package_manager) | ✔️ | ✔️ | ✔️ | ✔️ | | [**url_launcher_tizen**](packages/url_launcher) | ✔️ | ❌ | ✔️ | ❌ | No browser app | | [**video_player_tizen**](packages/video_player) | ✔️ | ✔️ | ⚠️ | ❌ | Functional limitations (see README)
TV emulator issue | | [**wakelock_tizen**](packages/wakelock) | ✔️ | ✔️ | ❌ | ❌ | Cannot override system display setting | diff --git a/packages/tizen_package_manager/.gitignore b/packages/tizen_package_manager/.gitignore new file mode 100644 index 000000000..e9dc58d3d --- /dev/null +++ b/packages/tizen_package_manager/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ diff --git a/packages/tizen_package_manager/CHANGELOG.md b/packages/tizen_package_manager/CHANGELOG.md new file mode 100644 index 000000000..802b87e00 --- /dev/null +++ b/packages/tizen_package_manager/CHANGELOG.md @@ -0,0 +1,4 @@ +## 0.1.0 + +* Initial release. + diff --git a/packages/tizen_package_manager/LICENSE b/packages/tizen_package_manager/LICENSE new file mode 100644 index 000000000..487f7b1dc --- /dev/null +++ b/packages/tizen_package_manager/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the names of the copyright holders nor the names of the + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/tizen_package_manager/README.md b/packages/tizen_package_manager/README.md new file mode 100644 index 000000000..ce36899b8 --- /dev/null +++ b/packages/tizen_package_manager/README.md @@ -0,0 +1,58 @@ +# tizen_package_manager + + [![pub package](https://img.shields.io/pub/v/tizen_package_manager.svg)](https://pub.dev/packages/tizen_package_manager) + +Tizen package manager API. Used for getting installed package info. + +## Usage + +To use this package, add `tizen_package_manager` as a dependency in your `pubspec.yaml` file. + +```yaml +dependencies: + tizen_package_manager: ^0.1.0 +``` + +### Retrieving specific package info + +To retrieve information of a specific package, use the `getPackageInfo` method which returns an instance of `PackageInfo`. + +```dart +var packageId = 'org.tizen.settings'; +var packageInfo = await PackageManager.getPackageInfo(packageId); +``` + +### Retrieving all packages' info + +To retrieve information of all packages installed on a Tizen device, use `getPackagesInfo` method. + +```dart +var packageList = await PackageManager.getPackagesInfo(); +for (var package in packageList) { + // Handle each package's info. +} +``` + +### Monitoring package events + +You can listen for package events using `onInstallProgressChanged`, `onUninstallProgressChanged`, and `onUpdateProgressChanged`. + +```dart +_subscription = PackageManager.onInstallProgressChanged.listen((event) { + // A package is being installed. +}); +... +_subscription.cancel(); +``` + +## Required privileges + +Privileges are required to use the package manager functionality. Add required privileges in tizen-manifest.xml of your application. + +```xml + + http://tizen.org/privilege/packagemanager.info + + http://tizen.org/privilege/packagemanager.admin + +``` diff --git a/packages/tizen_package_manager/example/.gitignore b/packages/tizen_package_manager/example/.gitignore new file mode 100644 index 000000000..0fa6b675c --- /dev/null +++ b/packages/tizen_package_manager/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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 diff --git a/packages/tizen_package_manager/example/README.md b/packages/tizen_package_manager/example/README.md new file mode 100644 index 000000000..480068aea --- /dev/null +++ b/packages/tizen_package_manager/example/README.md @@ -0,0 +1,7 @@ +# tizen_package_manager_example + +Demonstrates how to use the tizen_package_manager plugin. + +## Getting Started + +To run this app on your Tizen device, use [flutter-tizen](https://github.com/flutter-tizen/flutter-tizen). diff --git a/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart new file mode 100644 index 000000000..44219cb4f --- /dev/null +++ b/packages/tizen_package_manager/example/integration_test/tizen_package_manager_test.dart @@ -0,0 +1,32 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:tizen_package_manager/package_manager.dart'; + +import 'package:tizen_package_manager_example/main.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can get current package info', (WidgetTester tester) async { + // These test are based on the example app. + final PackageInfo info = + await PackageManager.getPackageInfo(currentPackageId); + expect(info.packageId, 'com.example.tizen_package_manager_example'); + expect(info.label, 'tizen_package_manager_example'); + expect(info.packageType, PackageType.tpk); + expect(info.version, '1.0.0'); + expect(info.isPreloaded, false); + expect(info.isSystem, false); + expect(info.isRemovable, true); + }); + + testWidgets('Can get all installed packages info', + (WidgetTester tester) async { + final List infos = await PackageManager.getPackagesInfo(); + expect(infos.isNotEmpty, true); + }); +} diff --git a/packages/tizen_package_manager/example/lib/main.dart b/packages/tizen_package_manager/example/lib/main.dart new file mode 100644 index 000000000..20c929946 --- /dev/null +++ b/packages/tizen_package_manager/example/lib/main.dart @@ -0,0 +1,188 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:tizen_package_manager/package_manager.dart'; + +/// The example app package ID. +const String currentPackageId = 'com.example.tizen_package_manager_example'; + +/// The main entry point for the UI app. +void main() { + runApp(const MyApp()); +} + +/// The main UI app widget. +class MyApp extends StatelessWidget { + /// The constructor of the main UI app widget. + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Package manager demo', + theme: ThemeData(primarySwatch: Colors.blue), + home: const _MyHomePage(), + ); + } +} + +class _MyHomePage extends StatefulWidget { + const _MyHomePage({Key? key}) : super(key: key); + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State<_MyHomePage> { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Package manager demo')), + body: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => + _CurrentPackageInfoScreen(), + ), + ); + }, + child: const Text('Current app package info'), + ), + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => _PackagesListScreen(), + ), + ); + }, + child: const Text('Installed packages list'), + ), + ], + ), + ), + ); + } +} + +class _CurrentPackageInfoScreen extends StatefulWidget { + @override + _CurrentPackageInfoScreenState createState() => + _CurrentPackageInfoScreenState(); +} + +class _CurrentPackageInfoScreenState extends State<_CurrentPackageInfoScreen> { + PackageInfo _packageInfo = PackageInfo( + packageId: '', + label: '', + packageType: PackageType.unknown, + iconPath: '', + version: '', + installedStorageType: '', + isSystem: false, + isPreloaded: false, + isRemovable: false, + ); + + @override + void initState() { + super.initState(); + + PackageManager.getPackageInfo(currentPackageId).then( + (PackageInfo packageInfo) { + setState(() { + _packageInfo = packageInfo; + }); + }, + ); + } + + Widget _infoTile(String title, String subtitle) { + return ListTile( + title: Text(title), + subtitle: Text(subtitle.isNotEmpty ? subtitle : 'Not set'), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Current app package info')), + body: ListView( + children: [ + _infoTile('Package ID', _packageInfo.packageId), + _infoTile('Label', _packageInfo.label), + _infoTile('Version', _packageInfo.version), + _infoTile('Package type', _packageInfo.packageType.name), + _infoTile('Icon path', _packageInfo.iconPath ?? ''), + _infoTile('Is system app', _packageInfo.isSystem.toString()), + _infoTile('Is preloaded app', _packageInfo.isPreloaded.toString()), + _infoTile('Is removable', _packageInfo.isRemovable.toString()), + ], + ), + ); + } +} + +class _PackagesListScreen extends StatefulWidget { + @override + _PackagesListScreenState createState() => _PackagesListScreenState(); +} + +class _PackagesListScreenState extends State<_PackagesListScreen> { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Package list')), + body: _PackagesListScreenContent(key: GlobalKey()), + ); + } +} + +class _PackagesListScreenContent extends StatelessWidget { + const _PackagesListScreenContent({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: PackageManager.getPackagesInfo(), + builder: (BuildContext context, AsyncSnapshot> data) { + if (data.data == null) { + return const Center(child: CircularProgressIndicator()); + } else { + final List packages = data.data!; + + return Scrollbar( + child: ListView.builder( + itemBuilder: (BuildContext context, int position) { + final PackageInfo package = packages[position]; + return Column( + children: [ + ListTile( + title: Text(package.label), + subtitle: Text('Package Id: ${package.packageId}\n' + 'Version: ${package.version}\n' + 'type: ${package.packageType}\n' + 'isSystem: ${package.isSystem}\n'), + ), + const Divider(height: 1.0) + ], + ); + }, + itemCount: packages.length, + ), + ); + } + }, + ); + } +} diff --git a/packages/tizen_package_manager/example/pubspec.yaml b/packages/tizen_package_manager/example/pubspec.yaml new file mode 100644 index 000000000..6db186ba4 --- /dev/null +++ b/packages/tizen_package_manager/example/pubspec.yaml @@ -0,0 +1,26 @@ +name: tizen_package_manager_example +description: Demonstrates how to use the tizen_package_manager plugin. +version: 1.0.0 +publish_to: 'none' + +environment: + sdk: ">=2.14.1 <3.0.0" + +dependencies: + flutter: + sdk: flutter + tizen_package_manager: + path: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + integration_test_tizen: + path: ../../integration_test/ + +flutter: + uses-material-design: true diff --git a/packages/tizen_package_manager/example/test_driver/integration_test.dart b/packages/tizen_package_manager/example/test_driver/integration_test.dart new file mode 100644 index 000000000..b38629cca --- /dev/null +++ b/packages/tizen_package_manager/example/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/tizen_package_manager/example/tizen/.gitignore b/packages/tizen_package_manager/example/tizen/.gitignore new file mode 100644 index 000000000..750f3af1b --- /dev/null +++ b/packages/tizen_package_manager/example/tizen/.gitignore @@ -0,0 +1,5 @@ +flutter/ +.vs/ +*.user +bin/ +obj/ diff --git a/packages/tizen_package_manager/example/tizen/App.cs b/packages/tizen_package_manager/example/tizen/App.cs new file mode 100644 index 000000000..6dd4a6356 --- /dev/null +++ b/packages/tizen_package_manager/example/tizen/App.cs @@ -0,0 +1,20 @@ +using Tizen.Flutter.Embedding; + +namespace Runner +{ + public class App : FlutterApplication + { + protected override void OnCreate() + { + base.OnCreate(); + + GeneratedPluginRegistrant.RegisterPlugins(this); + } + + static void Main(string[] args) + { + var app = new App(); + app.Run(args); + } + } +} diff --git a/packages/tizen_package_manager/example/tizen/Runner.csproj b/packages/tizen_package_manager/example/tizen/Runner.csproj new file mode 100644 index 000000000..c3c43aed9 --- /dev/null +++ b/packages/tizen_package_manager/example/tizen/Runner.csproj @@ -0,0 +1,26 @@ + + + + Exe + tizen40 + + + + portable + + + none + + + + + + + + + + %(RecursiveDir) + + + + diff --git a/packages/tizen_package_manager/example/tizen/shared/res/ic_launcher.png b/packages/tizen_package_manager/example/tizen/shared/res/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/packages/tizen_package_manager/example/tizen/shared/res/ic_launcher.png differ diff --git a/packages/tizen_package_manager/example/tizen/shared/res/org.example.simplehome.tpk b/packages/tizen_package_manager/example/tizen/shared/res/org.example.simplehome.tpk new file mode 100644 index 000000000..658f2ad1a Binary files /dev/null and b/packages/tizen_package_manager/example/tizen/shared/res/org.example.simplehome.tpk differ diff --git a/packages/tizen_package_manager/example/tizen/tizen-manifest.xml b/packages/tizen_package_manager/example/tizen/tizen-manifest.xml new file mode 100644 index 000000000..57965bb31 --- /dev/null +++ b/packages/tizen_package_manager/example/tizen/tizen-manifest.xml @@ -0,0 +1,13 @@ + + + + + + ic_launcher.png + + + + + http://tizen.org/privilege/packagemanager.info + + diff --git a/packages/tizen_package_manager/lib/package_manager.dart b/packages/tizen_package_manager/lib/package_manager.dart new file mode 100644 index 000000000..13b523c5a --- /dev/null +++ b/packages/tizen_package_manager/lib/package_manager.dart @@ -0,0 +1,304 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +/// Enumeration for the package type. +enum PackageType { + /// A special application package installed using the RPM spec. + /// Only some preloaded packages can have this type. + rpm, + + /// Tizen native application pacakge. + tpk, + + /// Tizen web/hybrid application package. + wgt, + + /// Unknown package. + unknown, +} + +/// Enumeration for the package manager event types. +enum PackageEventType { + /// Install event. + install, + + /// Uninstall event. + uninstall, + + /// Update event. + update, + + /// Move event. + move, + + /// Clear data event. + clearData, +} + +/// Enumeration for the package manager event state. +enum PackageEventState { + /// Processing started. + started, + + /// Processing state. + processing, + + /// Processing completed. + completed, + + /// Processing failed. + failed, +} + +/// The package manager provides information about installed packages. +/// This information includes the pacakge name, label, path of icon, version, +/// type and installed storage. +/// +/// For detailed information on Tizen's Package Manager, see: +/// https://docs.tizen.org/application/dotnet/guides/app-management/package-manager/ +class PackageManager { + PackageManager._(); + + static const MethodChannel _channel = MethodChannel('tizen/package_manager'); + + static const EventChannel _installEventChannel = + EventChannel('tizen/package_manager/install_event'); + + static const EventChannel _uninstallEventChannel = + EventChannel('tizen/package_manager/uninstall_event'); + + static const EventChannel _updateEventChannel = + EventChannel('tizen/package_manager/update_event'); + + /// Gets the package information for the given package ID. + static Future getPackageInfo(String packageId) async { + if (packageId.isEmpty) { + throw ArgumentError('Must not be empty', 'packageId'); + } + + final Map map = await _channel + .invokeMapMethod( + 'getPackage', {'packageId': packageId}) ?? + {}; + + return PackageInfo.fromMap(map); + } + + /// Retrieves the package information of all installed packages. + static Future> getPackagesInfo() async { + final List? packages = + await _channel.invokeMethod>('getPackages'); + + final List list = []; + if (packages != null) { + for (final dynamic package in packages) { + list.add(PackageInfo.fromMap(package)); + } + } + return list; + } + + /// Installs the package located at the given path. + /// + /// The `http://tizen.org/privilege/packagemanager.admin` platform privilege + /// is required to use this API. + static Future install(String packagePath) async { + if (packagePath.isEmpty) { + throw ArgumentError('Must not be empty', 'packagePath'); + } + + final bool ret = await _channel.invokeMethod( + 'install', {'path': packagePath}) ?? + false; + return ret; + } + + /// Uninstalls the package with the given package ID. + /// + /// The `http://tizen.org/privilege/packagemanager.admin` platform privilege + /// is required to use this API. + static Future uninstall(String packageId) async { + if (packageId.isEmpty) { + throw ArgumentError('Must not be empty', 'packageId'); + } + + final bool ret = await _channel.invokeMethod( + 'uninstall', {'packageId': packageId}) ?? + false; + return ret; + } + + /// A stream of events occurring when a package is getting installed + /// and the progress of the request to the package manager is changed. + static Stream get onInstallProgressChanged => + _installEventChannel + .receiveBroadcastStream() + .map((dynamic event) => PackageEvent.fromMap(event)); + + /// A stream of events occurring when a package is getting uninstalled + /// and the progress of the request to the package manager is changed. + static Stream get onUninstallProgressChanged => + _uninstallEventChannel + .receiveBroadcastStream() + .map((dynamic event) => PackageEvent.fromMap(event)); + + /// A stream of events occurring when a package is getting updated + /// and the progress of the request to the package manager is changed. + static Stream get onUpdateProgressChanged => _updateEventChannel + .receiveBroadcastStream() + .map((dynamic event) => PackageEvent.fromMap(event)); +} + +/// Represents information of specific package. +class PackageInfo { + /// Creates an instance of [PackageInfo] with the given parameters. + PackageInfo({ + required this.packageId, + required this.label, + required this.packageType, + required this.iconPath, + required this.version, + required this.installedStorageType, + required this.isSystem, + required this.isPreloaded, + required this.isRemovable, + }); + + /// The package ID. + final String packageId; + + /// Label of the package. + final String label; + + /// Type of the package. + final PackageType packageType; + + /// The path to the icon image. + final String? iconPath; + + /// Version of the package. + final String version; + + /// Installed storage type for the package. + /// the return value is either internal storage or external storage. + final String installedStorageType; + + /// Whether the package is a system package. + final bool isSystem; + + /// Whether the package is a preload package. + final bool isPreloaded; + + /// Whether the package is a removable package. + final bool isRemovable; + + /// Creates an instance of [PackageInfo] with the map parameter. + static PackageInfo fromMap(dynamic map) { + final Object? packageType = map['type']; + PackageType type = PackageType.unknown; + switch (packageType) { + case 'rpm': + type = PackageType.rpm; + break; + case 'wgt': + type = PackageType.wgt; + break; + case 'tpk': + type = PackageType.tpk; + break; + case 'unknown': + type = PackageType.unknown; + } + + return PackageInfo( + packageId: map['packageId'] as String, + label: map['label'] as String, + iconPath: map['iconPath'] as String?, + packageType: type, + version: map['version'] as String, + installedStorageType: map['installedStorageType'] as String, + isSystem: map['isSystem'] as bool, + isPreloaded: map['isPreloaded'] as bool, + isRemovable: map['isRemovable'] as bool, + ); + } +} + +/// Represents the event arguments of [PackageManager] events. +class PackageEvent { + /// Creates an instance of [PackageEvent] with the given parameters. + PackageEvent({ + required this.packageId, + required this.packageType, + required this.eventType, + required this.eventState, + required this.progress, + }); + + /// The package ID. + final String packageId; + + /// Type of the package. + final String packageType; + + /// The package manager event types + final PackageEventType eventType; + + /// The package manager event state. + final PackageEventState eventState; + + /// Progress for the request being processed by the package manager (in percent). + final int progress; + + /// Creates an instance of [PackageEvent] with the map parameter. + static PackageEvent fromMap(dynamic map) { + PackageEventType type = PackageEventType.install; + PackageEventState state = PackageEventState.started; + final String eventType = map['eventType'] as String; + final String eventState = map['eventState'] as String; + + switch (eventType) { + case 'install': + type = PackageEventType.install; + break; + case 'uninstall': + type = PackageEventType.uninstall; + break; + case 'update': + type = PackageEventType.update; + break; + case 'cleardata': + type = PackageEventType.clearData; + break; + case 'move': + type = PackageEventType.move; + } + + switch (eventState) { + case 'started': + state = PackageEventState.started; + break; + case 'processing': + state = PackageEventState.processing; + break; + case 'completed': + state = PackageEventState.completed; + break; + case 'failed': + state = PackageEventState.failed; + } + + return PackageEvent( + packageId: map['packageId'] as String, + packageType: map['type'] as String, + eventType: type, + eventState: state, + progress: map['progress'] as int, + ); + } +} diff --git a/packages/tizen_package_manager/pubspec.yaml b/packages/tizen_package_manager/pubspec.yaml new file mode 100644 index 000000000..34e594536 --- /dev/null +++ b/packages/tizen_package_manager/pubspec.yaml @@ -0,0 +1,21 @@ +name: tizen_package_manager +description: Tizen package manager APIs. Used to get detailed information on the installed packages on the Tizen device. +homepage: https://github.com/flutter-tizen/plugins +repository: https://github.com/flutter-tizen/plugins/tree/master/packages/tizen_package_manager +version: 0.1.0 + +environment: + sdk: ">=2.15.1 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + +flutter: + plugin: + platforms: + tizen: + pluginClass: TizenPackageManagerPlugin + fileName: tizen_package_manager_plugin.h + diff --git a/packages/tizen_package_manager/tizen/.gitignore b/packages/tizen_package_manager/tizen/.gitignore new file mode 100644 index 000000000..a2a7d62b1 --- /dev/null +++ b/packages/tizen_package_manager/tizen/.gitignore @@ -0,0 +1,5 @@ +.cproject +.sign +crash-info/ +Debug/ +Release/ diff --git a/packages/tizen_package_manager/tizen/inc/tizen_package_manager_plugin.h b/packages/tizen_package_manager/tizen/inc/tizen_package_manager_plugin.h new file mode 100644 index 000000000..da92b5561 --- /dev/null +++ b/packages/tizen_package_manager/tizen/inc/tizen_package_manager_plugin.h @@ -0,0 +1,27 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_PLUGIN_TIZEN_PACKAGE_MANAGER_PLUGIN_H_ +#define FLUTTER_PLUGIN_TIZEN_PACKAGE_MANAGER_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void TizenPackageManagerPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_TIZEN_PACKAGE_MANAGER_PLUGIN_H_ diff --git a/packages/tizen_package_manager/tizen/project_def.prop b/packages/tizen_package_manager/tizen/project_def.prop new file mode 100644 index 000000000..06562b4d0 --- /dev/null +++ b/packages/tizen_package_manager/tizen/project_def.prop @@ -0,0 +1,24 @@ +# See https://docs.tizen.org/application/tizen-studio/native-tools/project-conversion +# for details. + +APPNAME = tizen_package_manager_plugin +type = staticLib +profile = common-4.0 + +# Source files +USER_SRCS += src/*.cc + +# User defines +USER_DEFS = +USER_UNDEFS = +USER_CPP_DEFS = FLUTTER_PLUGIN_IMPL +USER_CPP_UNDEFS = + +# Compiler flags +USER_CFLAGS_MISC = +USER_CPPFLAGS_MISC = + +# User includes +USER_INC_DIRS = inc src +USER_INC_FILES = +USER_CPP_INC_FILES = diff --git a/packages/tizen_package_manager/tizen/src/log.h b/packages/tizen_package_manager/tizen/src/log.h new file mode 100644 index 000000000..04abb8b4e --- /dev/null +++ b/packages/tizen_package_manager/tizen/src/log.h @@ -0,0 +1,24 @@ +#ifndef __LOG_H__ +#define __LOG_H__ + +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "TizenPackageManagerPlugin" + +#ifndef __MODULE__ +#define __MODULE__ strrchr("/" __FILE__, '/') + 1 +#endif + +#define LOG(prio, fmt, arg...) \ + dlog_print(prio, LOG_TAG, "%s: %s(%d) > " fmt, __MODULE__, __func__, \ + __LINE__, ##arg) + +#define LOG_DEBUG(fmt, args...) LOG(DLOG_DEBUG, fmt, ##args) +#define LOG_INFO(fmt, args...) LOG(DLOG_INFO, fmt, ##args) +#define LOG_WARN(fmt, args...) LOG(DLOG_WARN, fmt, ##args) +#define LOG_ERROR(fmt, args...) LOG(DLOG_ERROR, fmt, ##args) + +#endif // __LOG_H__ diff --git a/packages/tizen_package_manager/tizen/src/package_manager_utils.cc b/packages/tizen_package_manager/tizen/src/package_manager_utils.cc new file mode 100644 index 000000000..0654a4c7a --- /dev/null +++ b/packages/tizen_package_manager/tizen/src/package_manager_utils.cc @@ -0,0 +1,173 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "package_manager_utils.h" + +namespace package_manager_utils { + +bool ExtractValueFromMap(const flutter::EncodableValue &arguments, + const char *key, std::string &out_value) { + if (std::holds_alternative(arguments)) { + flutter::EncodableMap map = std::get(arguments); + auto iter = map.find(flutter::EncodableValue(key)); + if (iter != map.end() && !iter->second.IsNull()) { + if (auto pval = std::get_if(&iter->second)) { + out_value = *pval; + return true; + } + } + } + return false; +} + +int GetPackageData(package_info_h package_info, flutter::EncodableMap &value) { + char *pkg_name = nullptr; + char *label = nullptr; + char *type = nullptr; + char *icon_path = nullptr; + char *version = nullptr; + package_info_installed_storage_type_e installed_storage_type = + PACKAGE_INFO_INTERNAL_STORAGE; + bool is_system = false; + bool is_preloaded = false; + bool is_removable = true; + + int ret = package_info_get_package(package_info, &pkg_name); + if (ret != PACKAGE_MANAGER_ERROR_NONE || pkg_name == nullptr) { + LOG_ERROR("get package name error! : %s", get_error_message(ret)); + goto cleanup; + } + + ret = package_info_get_label(package_info, &label); + if (ret != PACKAGE_MANAGER_ERROR_NONE || label == nullptr) { + LOG_ERROR("get package label error! : %s", get_error_message(ret)); + goto cleanup; + } + + ret = package_info_get_type(package_info, &type); + if (ret != PACKAGE_MANAGER_ERROR_NONE || type == nullptr) { + LOG_ERROR("get package type error! : %s", get_error_message(ret)); + goto cleanup; + } + + ret = package_info_get_icon(package_info, &icon_path); + if (ret != PACKAGE_MANAGER_ERROR_NONE || icon_path == nullptr) { + // because some service app doesn't have icon, + // just print error log, and pass it + LOG_ERROR("get icon path error! : %s", get_error_message(ret)); + } + + ret = package_info_get_version(package_info, &version); + if (ret != PACKAGE_MANAGER_ERROR_NONE || version == nullptr) { + LOG_ERROR("get version error! : %s", get_error_message(ret)); + goto cleanup; + } + + ret = + package_info_get_installed_storage(package_info, &installed_storage_type); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + LOG_ERROR("get installed storage error! : %s", get_error_message(ret)); + goto cleanup; + } + + ret = package_info_is_system_package(package_info, &is_system); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + LOG_ERROR("check system package error! : %s", get_error_message(ret)); + goto cleanup; + } + + ret = package_info_is_preload_package(package_info, &is_preloaded); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + LOG_ERROR("check preload package error! : %s", get_error_message(ret)); + goto cleanup; + } + + ret = package_info_is_removable_package(package_info, &is_removable); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + LOG_ERROR("check removable package error! : %s", get_error_message(ret)); + goto cleanup; + } + + value[flutter::EncodableValue("packageId")] = + flutter::EncodableValue(std::string(pkg_name)); + value[flutter::EncodableValue("label")] = + flutter::EncodableValue(std::string(label)); + value[flutter::EncodableValue("type")] = + flutter::EncodableValue(std::string(type)); + value[flutter::EncodableValue("version")] = + flutter::EncodableValue(std::string(version)); + value[flutter::EncodableValue("installedStorageType")] = + flutter::EncodableValue(StorageTypeToString(installed_storage_type)); + value[flutter::EncodableValue("isSystem")] = + flutter::EncodableValue(is_system); + value[flutter::EncodableValue("isPreloaded")] = + flutter::EncodableValue(is_preloaded); + value[flutter::EncodableValue("isRemovable")] = + flutter::EncodableValue(is_removable); + if (icon_path) { + value[flutter::EncodableValue("iconPath")] = + flutter::EncodableValue(std::string(icon_path)); + } + +cleanup: + if (pkg_name) { + free(pkg_name); + } + if (label) { + free(label); + } + if (type) { + free(type); + } + if (icon_path) { + free(icon_path); + } + if (version) { + free(version); + } + + return ret; +} + +std::string StorageTypeToString(package_info_installed_storage_type_e value) { + switch (value) { + case PACKAGE_INFO_EXTERNAL_STORAGE: + return "External storage"; + case PACKAGE_INFO_INTERNAL_STORAGE: + default: + return "Internal storage"; + } +} + +std::string PacakgeEventTypeToString(package_manager_event_type_e type) { + switch (type) { + case PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL: + return "uninstall"; + case PACKAGE_MANAGER_EVENT_TYPE_UPDATE: + return "update"; + case PACKAGE_MANAGER_EVENT_TYPE_MOVE: + return "move"; + case PACKAGE_MANAGER_EVENT_TYPE_CLEAR: + return "cleardata"; + case PACKAGE_MANAGER_EVENT_TYPE_INSTALL: + default: + return "install"; + } +} + +std::string PacakgeEventStateToString(package_manager_event_state_e state) { + switch (state) { + case PACKAGE_MANAGER_EVENT_STATE_STARTED: + return "started"; + case PACKAGE_MANAGER_EVENT_STATE_PROCESSING: + return "processing"; + case PACKAGE_MANAGER_EVENT_STATE_FAILED: + return "failed"; + case PACKAGE_MANAGER_EVENT_STATE_COMPLETED: + default: + return "completed"; + } +} + +} // namespace package_manager_utils diff --git a/packages/tizen_package_manager/tizen/src/package_manager_utils.h b/packages/tizen_package_manager/tizen/src/package_manager_utils.h new file mode 100644 index 000000000..4d3b594f6 --- /dev/null +++ b/packages/tizen_package_manager/tizen/src/package_manager_utils.h @@ -0,0 +1,30 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_PLUGIN_PACKAGE_MANAGER_UTILS_H_ +#define FLUTTER_PLUGIN_PACKAGE_MANAGER_UTILS_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "log.h" + +namespace package_manager_utils { +bool ExtractValueFromMap(const flutter::EncodableValue &arguments, + const char *key, std::string &out_value); +int GetPackageData(package_info_h package_info, flutter::EncodableMap &value); + +std::string StorageTypeToString(package_info_installed_storage_type_e value); +std::string PacakgeEventTypeToString(package_manager_event_type_e type); +std::string PacakgeEventStateToString(package_manager_event_state_e state); +} // namespace package_manager_utils + +#endif diff --git a/packages/tizen_package_manager/tizen/src/tizen_package_manager_plugin.cc b/packages/tizen_package_manager/tizen/src/tizen_package_manager_plugin.cc new file mode 100644 index 000000000..36f5e5268 --- /dev/null +++ b/packages/tizen_package_manager/tizen/src/tizen_package_manager_plugin.cc @@ -0,0 +1,421 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tizen_package_manager_plugin.h" + +#include +#include +#include + +#include "package_manager_utils.h" + +const char kPackageTypeUnkown[] = "unknown"; +const char kPackageTypeTpk[] = "tpk"; +const char kPackageTypeWgt[] = "wgt"; + +class TizenPackageManagerPlugin : public flutter::Plugin { + public: + using MethodResultPtr = + std::unique_ptr>; + + static void RegisterWithRegistrar(flutter::PluginRegistrar *registrar) { + auto plugin = std::make_unique(); + plugin->SetupChannels(registrar); + registrar->AddPlugin(std::move(plugin)); + } + + TizenPackageManagerPlugin() {} + + virtual ~TizenPackageManagerPlugin() { UnregisterObserver(); } + + private: + void HandleMethodCall( + const flutter::MethodCall &method_call, + MethodResultPtr result) { + const auto &arguments = *method_call.arguments(); + + if (method_call.method_name().compare("getPackage") == 0) { + GetPackageInfo(arguments, std::move(result)); + } else if (method_call.method_name().compare("getPackages") == 0) { + GetAllPackagesInfo(std::move(result)); + } else if (method_call.method_name().compare("install") == 0) { + Install(arguments, std::move(result)); + } else if (method_call.method_name().compare("uninstall") == 0) { + Uninstall(arguments, std::move(result)); + } else { + result->NotImplemented(); + } + } + + void RegisterObserver( + std::unique_ptr> &&events) { + int ret = PACKAGE_MANAGER_ERROR_NONE; + LOG_INFO("RegisterObserver"); + + if (package_manager_h_ == nullptr) { + ret = package_manager_create(&package_manager_h_); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + char *err_msg = get_error_message(ret); + LOG_ERROR("Failed package_manager_create : %s", err_msg); + events->Error("Failed to create package manager handle", + std::string(err_msg)); + return; + } + } + + package_manager_set_event_status(package_manager_h_, + PACKAGE_MANAGER_STATUS_TYPE_INSTALL | + PACKAGE_MANAGER_STATUS_TYPE_UNINSTALL | + PACKAGE_MANAGER_STATUS_TYPE_UPGRADE); + ret = package_manager_set_event_cb(package_manager_h_, PackageEventCB, + (void *)this); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + char *err_msg = get_error_message(ret); + LOG_ERROR("Failed package_manager_set_event_cb : %s", err_msg); + events->Error("Failed to add callback", std::string(err_msg)); + return; + } + is_event_callback_registered_ = true; + } + + void UnregisterObserver() { + LOG_INFO("UnregisterObserver"); + if (is_event_callback_registered_ && package_manager_h_) { + int ret = package_manager_unset_event_cb(package_manager_h_); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + LOG_ERROR("Failed package_manager_unset_event_cb : %s", + get_error_message(ret)); + } + + package_manager_destroy(package_manager_h_); + package_manager_h_ = nullptr; + is_event_callback_registered_ = false; + } + install_events_ = nullptr; + uninstall_events_ = nullptr; + update_events_ = nullptr; + } + + void GetPackageInfo(const flutter::EncodableValue &arguments, + MethodResultPtr result) { + std::string id = ""; + const char *package_id; + char *err_msg; + package_info_h package_info = nullptr; + flutter::EncodableMap value; + + if (!package_manager_utils::ExtractValueFromMap(arguments, "packageId", + id)) { + result->Error("InvalidArguments", "Please check packageId"); + return; + } + package_id = id.c_str(); + LOG_INFO("GetPackageInfo() package_id : %s", package_id); + + int ret = package_info_create(package_id, &package_info); + if (ret != PACKAGE_MANAGER_ERROR_NONE || package_info == nullptr) { + err_msg = get_error_message(ret); + LOG_ERROR("Failed to get package_info handler : %s", err_msg); + result->Error(std::to_string(ret), + "Failed to create package_info handler.", + flutter::EncodableValue(std::string(err_msg))); + goto cleanup; + } + + ret = package_manager_utils::GetPackageData(package_info, value); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + err_msg = get_error_message(ret); + result->Error(std::to_string(ret), "Failed to package info.", + flutter::EncodableValue(std::string(err_msg))); + goto cleanup; + } + result->Success(flutter::EncodableValue(value)); + + cleanup: + if (package_info) { + package_info_destroy(package_info); + } + } + + void GetAllPackagesInfo(MethodResultPtr result) { + LOG_INFO("GetAllPackagesInfo()"); + packages_.erase(packages_.begin(), packages_.end()); + int ret = package_manager_foreach_package_info(PackageInfoCB, (void *)this); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + char *err_msg = get_error_message(ret); + LOG_ERROR("package_manager_foreach_package_info error: %s", err_msg); + result->Error(std::to_string(ret), + "package_manager_foreach_package_info error.", + flutter::EncodableValue(std::string(err_msg))); + } + result->Success(flutter::EncodableValue(packages_)); + } + + void Install(const flutter::EncodableValue &arguments, + MethodResultPtr result) { + std::string path = ""; + const char *package_path; + char *err_msg; + package_manager_request_h package_manager_request = nullptr; + int request_id; + + if (!package_manager_utils::ExtractValueFromMap(arguments, "path", path)) { + result->Error("InvalidArguments", "Please check path"); + return; + } + package_path = path.c_str(); + LOG_INFO("Install() package_path : %s", package_path); + + int ret = package_manager_request_create(&package_manager_request); + if (ret != PACKAGE_MANAGER_ERROR_NONE || + package_manager_request == nullptr) { + err_msg = get_error_message(ret); + LOG_ERROR("Failed to get package_manager_request handler : %s", err_msg); + result->Error(std::to_string(ret), + "Failed to create package_manager_request handler.", + flutter::EncodableValue(std::string(err_msg))); + goto cleanup; + } + + ret = package_manager_request_install(package_manager_request, package_path, + &request_id); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + err_msg = get_error_message(ret); + result->Error(std::to_string(ret), "Failed to install.", + flutter::EncodableValue(std::string(err_msg))); + goto cleanup; + } + result->Success(flutter::EncodableValue(true)); + + cleanup: + if (package_manager_request) { + package_manager_request_destroy(package_manager_request); + } + } + + void Uninstall(const flutter::EncodableValue &arguments, + MethodResultPtr result) { + std::string id = ""; + const char *package_id; + char *err_msg; + package_manager_request_h package_manager_request = nullptr; + int request_id; + + if (!package_manager_utils::ExtractValueFromMap(arguments, "packageId", + id)) { + result->Error("InvalidArguments", "Please check packageId"); + return; + } + package_id = id.c_str(); + LOG_INFO("Uninstall() package_id : %s", package_id); + + int ret = package_manager_request_create(&package_manager_request); + if (ret != PACKAGE_MANAGER_ERROR_NONE || + package_manager_request == nullptr) { + err_msg = get_error_message(ret); + LOG_ERROR("Failed to get package_manager_request handler : %s", err_msg); + result->Error(std::to_string(ret), + "Failed to create package_manager_request handler.", + flutter::EncodableValue(std::string(err_msg))); + goto cleanup; + } + + ret = package_manager_request_set_type(package_manager_request, + kPackageTypeUnkown); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + err_msg = get_error_message(ret); + LOG_ERROR("Failed to set request type : %s", err_msg); + result->Error(std::to_string(ret), "Failed to set request type.", + flutter::EncodableValue(std::string(err_msg))); + goto cleanup; + } + + ret = package_manager_request_uninstall(package_manager_request, package_id, + &request_id); + if (ret != PACKAGE_MANAGER_ERROR_NONE) { + err_msg = get_error_message(ret); + result->Error(std::to_string(ret), "Failed to uninstall.", + flutter::EncodableValue(std::string(err_msg))); + goto cleanup; + } + result->Success(flutter::EncodableValue(true)); + + cleanup: + if (package_manager_request) { + package_manager_request_destroy(package_manager_request); + } + } + + void SetupChannels(flutter::PluginRegistrar *registrar) { + auto method_channel = + std::make_unique>( + registrar->messenger(), "tizen/package_manager", + &flutter::StandardMethodCodec::GetInstance()); + + method_channel->SetMethodCallHandler([this](const auto &call, auto result) { + this->HandleMethodCall(call, std::move(result)); + }); + + auto install_event_channel = + std::make_unique>( + registrar->messenger(), "tizen/package_manager/install_event", + &flutter::StandardMethodCodec::GetInstance()); + + auto uninstall_event_channel = + std::make_unique>( + registrar->messenger(), "tizen/package_manager/uninstall_event", + &flutter::StandardMethodCodec::GetInstance()); + + auto update_event_channel = + std::make_unique>( + registrar->messenger(), "tizen/package_manager/update_event", + &flutter::StandardMethodCodec::GetInstance()); + + auto install_event_channel_handler = + std::make_unique>( + [this](const flutter::EncodableValue *arguments, + std::unique_ptr> &&events) + -> std::unique_ptr> { + LOG_INFO("OnListen install"); + install_events_ = std::move(events); + if (registered_cnt_ == 0) { + this->RegisterObserver(std::move(events)); + } + registered_cnt_++; + return nullptr; + }, + [this](const flutter::EncodableValue *arguments) + -> std::unique_ptr> { + registered_cnt_--; + LOG_INFO("OnCancel install"); + if (registered_cnt_ == 0) { + this->UnregisterObserver(); + } + install_events_ = nullptr; + return nullptr; + }); + + auto uninstall_event_channel_handler = + std::make_unique>( + [this](const flutter::EncodableValue *arguments, + std::unique_ptr> &&events) + -> std::unique_ptr> { + LOG_INFO("OnListen uninstall"); + uninstall_events_ = std::move(events); + if (registered_cnt_ == 0) { + this->RegisterObserver(std::move(events)); + } + registered_cnt_++; + return nullptr; + }, + [this](const flutter::EncodableValue *arguments) + -> std::unique_ptr> { + LOG_INFO("OnCancel uninstall"); + registered_cnt_--; + if (registered_cnt_ == 0) { + this->UnregisterObserver(); + } + uninstall_events_ = nullptr; + return nullptr; + }); + + auto update_event_channel_handler = + std::make_unique>( + [this](const flutter::EncodableValue *arguments, + std::unique_ptr> &&events) + -> std::unique_ptr> { + LOG_INFO("OnListen update"); + update_events_ = std::move(events); + if (registered_cnt_ == 0) { + this->RegisterObserver(std::move(events)); + } + registered_cnt_++; + return nullptr; + }, + [this](const flutter::EncodableValue *arguments) + -> std::unique_ptr> { + LOG_INFO("OnCancel update"); + registered_cnt_--; + if (registered_cnt_ == 0) { + this->UnregisterObserver(); + } + update_events_ = nullptr; + return nullptr; + }); + + install_event_channel->SetStreamHandler( + std::move(install_event_channel_handler)); + uninstall_event_channel->SetStreamHandler( + std::move(uninstall_event_channel_handler)); + update_event_channel->SetStreamHandler( + std::move(update_event_channel_handler)); + } + + static bool PackageInfoCB(package_info_h package_info, void *user_data) { + if (package_info) { + TizenPackageManagerPlugin *plugin = + (TizenPackageManagerPlugin *)user_data; + flutter::EncodableMap value; + int ret = package_manager_utils::GetPackageData(package_info, value); + if (ret == PACKAGE_MANAGER_ERROR_NONE) { + plugin->packages_.push_back(flutter::EncodableValue(value)); + } + return true; + } + return false; + } + + static void PackageEventCB(const char *type, const char *package, + package_manager_event_type_e event_type, + package_manager_event_state_e event_state, + int progress, package_manager_error_e error, + void *user_data) { + LOG_INFO("PackageEventCB, packageId : %s, type: %s", package, type); + LOG_INFO( + "event_type: %s, event_state: %s, progress : %d ", + package_manager_utils::PacakgeEventTypeToString(event_type).c_str(), + package_manager_utils::PacakgeEventStateToString(event_state).c_str(), + progress); + + TizenPackageManagerPlugin *plugin = (TizenPackageManagerPlugin *)user_data; + flutter::EncodableMap msg; + msg[flutter::EncodableValue("packageId")] = + flutter::EncodableValue(std::string(package)); + msg[flutter::EncodableValue("type")] = + flutter::EncodableValue(std::string(type)); + msg[flutter::EncodableValue("eventType")] = flutter::EncodableValue( + package_manager_utils::PacakgeEventTypeToString(event_type)); + msg[flutter::EncodableValue("eventState")] = flutter::EncodableValue( + package_manager_utils::PacakgeEventStateToString(event_state)); + msg[flutter::EncodableValue("progress")] = + flutter::EncodableValue(progress); + + if (event_type == PACKAGE_MANAGER_EVENT_TYPE_INSTALL && + plugin->install_events_) { + plugin->install_events_->Success(flutter::EncodableValue(msg)); + } else if (event_type == PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL && + plugin->uninstall_events_) { + plugin->uninstall_events_->Success(flutter::EncodableValue(msg)); + } else if (event_type == PACKAGE_MANAGER_EVENT_TYPE_UPDATE && + plugin->update_events_) { + plugin->update_events_->Success(flutter::EncodableValue(msg)); + } + } + + flutter::EncodableList packages_; + std::unique_ptr> install_events_; + std::unique_ptr> + uninstall_events_; + std::unique_ptr> update_events_; + bool is_event_callback_registered_ = false; + int registered_cnt_ = 0; + package_manager_h package_manager_h_ = nullptr; +}; + +void TizenPackageManagerPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + TizenPackageManagerPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +}