diff --git a/packages/path_provider/CHANGELOG.md b/packages/path_provider/CHANGELOG.md new file mode 100644 index 000000000..32f490b5f --- /dev/null +++ b/packages/path_provider/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release diff --git a/packages/path_provider/LICENSE b/packages/path_provider/LICENSE new file mode 100644 index 000000000..7f6595961 --- /dev/null +++ b/packages/path_provider/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved. +Copyright (c) 2019 The Chromium Authors. 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/path_provider/README.md b/packages/path_provider/README.md new file mode 100644 index 000000000..c9290c139 --- /dev/null +++ b/packages/path_provider/README.md @@ -0,0 +1,51 @@ +# path_provider_tizen + +The Tizen implementation of [`path_provider`](https://github.com/flutter/plugins/tree/master/packages/path_provider). + +## Usage + +This package is not an _endorsed_ implementation of `path_provider`. Therefore, you have to include `path_provider_tizen` alongside `path_provider` as dependencies in your `pubspec.yaml` file. + +```yaml +dependencies: + path_provider: ^1.6.10 + path_provider_tizen: ^1.0.0 +``` + +Then you can import `path_provider` in your Dart code: + +```dart +import 'package:path_provider/path_provider.dart'; +``` + +For detailed usage, see https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider#usage. + +## Required privileges + +- To access paths returned by + + - `getExternalStoragePaths` + - `getDownloadsPath` + + add below lines under the `` section in your `tizen-manifest.xml` file, + + ```xml + + http://tizen.org/privilege/mediastorage + + ``` + + and also acquire `Permission.accessMediaLocation` using the [`permission_handler`](https://pub.dev/packages/permission_handler_tizen) plugin (to be available soon). The permission is already granted on TV devices by default. + +- To access paths returned by + + - `getExternalDataPath` + - `getExternalCachePath` + + add below lines under the `` section in your `tizen-manifest.xml` file. + + ```xml + + http://tizen.org/privilege/externalstorage.appdata + + ``` diff --git a/packages/path_provider/example/.gitignore b/packages/path_provider/example/.gitignore new file mode 100644 index 000000000..9d532b18a --- /dev/null +++ b/packages/path_provider/example/.gitignore @@ -0,0 +1,41 @@ +# 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 diff --git a/packages/path_provider/example/README.md b/packages/path_provider/example/README.md new file mode 100644 index 000000000..64925575f --- /dev/null +++ b/packages/path_provider/example/README.md @@ -0,0 +1,7 @@ +# path_provider_tizen_example + +Demonstrates how to use the path_provider_tizen 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/path_provider/example/integration_test/path_provider_test.dart b/packages/path_provider/example/integration_test/path_provider_test.dart new file mode 100644 index 000000000..5268478ff --- /dev/null +++ b/packages/path_provider/example/integration_test/path_provider_test.dart @@ -0,0 +1,108 @@ +// Copyright 2019 the Chromium project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'dart:io'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:permission_handler/permission_handler.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('getTemporaryDirectory', (WidgetTester tester) async { + final Directory result = await getTemporaryDirectory(); + _verifySampleFile(result, 'temporaryDirectory'); + }); + + testWidgets('getApplicationDocumentsDirectory', (WidgetTester tester) async { + final Directory result = await getApplicationDocumentsDirectory(); + _verifySampleFile(result, 'applicationDocuments'); + }); + + testWidgets('getApplicationSupportDirectory', (WidgetTester tester) async { + final Directory result = await getApplicationSupportDirectory(); + _verifySampleFile(result, 'applicationSupport'); + }); + + testWidgets('getLibraryDirectory', (WidgetTester tester) async { + if (Platform.isIOS) { + final Directory result = await getLibraryDirectory(); + _verifySampleFile(result, 'library'); + } else if (Platform.isAndroid) { + final Future result = getLibraryDirectory(); + expect(result, throwsA(isInstanceOf())); + } + }); + + testWidgets('getExternalStorageDirectory', (WidgetTester tester) async { + if (Platform.isIOS) { + final Future result = getExternalStorageDirectory(); + expect(result, throwsA(isInstanceOf())); + } else if (Platform.isAndroid) { + final Directory result = await getExternalStorageDirectory(); + _verifySampleFile(result, 'externalStorage'); + } + }); + + testWidgets('getExternalCacheDirectories', (WidgetTester tester) async { + if (Platform.isIOS) { + final Future> result = getExternalCacheDirectories(); + expect(result, throwsA(isInstanceOf())); + } else if (Platform.isAndroid) { + final List directories = await getExternalCacheDirectories(); + for (Directory result in directories) { + _verifySampleFile(result, 'externalCache'); + } + } + }); + + final List _allDirs = [ + null, + StorageDirectory.music, + StorageDirectory.podcasts, + StorageDirectory.alarms, + StorageDirectory.notifications, + StorageDirectory.pictures, + StorageDirectory.movies, + ]; + + for (StorageDirectory type in _allDirs) { + test('getExternalStorageDirectories (type: $type)', () async { + if (Platform.isIOS) { + final Future> result = + getExternalStorageDirectories(type: null); + expect(result, throwsA(isInstanceOf())); + } else { + // Remove when testing on TV. + if (!await Permission.accessMediaLocation.isGranted) { + await Permission.accessMediaLocation.request(); + } + final List directories = + await getExternalStorageDirectories(type: type); + for (Directory result in directories) { + _verifySampleFile(result, '$type'); + } + } + }); + } +} + +/// Verify a file called [name] in [directory] by recreating it with test +/// contents when necessary. +void _verifySampleFile(Directory directory, String name) { + final File file = File('${directory.path}/$name'); + + if (file.existsSync()) { + file.deleteSync(); + expect(file.existsSync(), isFalse); + } + + file.writeAsStringSync('Hello world!'); + expect(file.readAsStringSync(), 'Hello world!'); + expect(directory.listSync(), isNotEmpty); + file.deleteSync(); +} diff --git a/packages/path_provider/example/lib/main.dart b/packages/path_provider/example/lib/main.dart new file mode 100644 index 000000000..a100ee110 --- /dev/null +++ b/packages/path_provider/example/lib/main.dart @@ -0,0 +1,230 @@ +// Copyright 2019 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Path Provider', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: MyHomePage(title: 'Path Provider'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + Future _tempDirectory; + Future _appSupportDirectory; + Future _appLibraryDirectory; + Future _appDocumentsDirectory; + Future _appDownloadsDirectory; + Future _externalDocumentsDirectory; + Future> _externalStorageDirectories; + Future> _externalCacheDirectories; + + void _requestTempDirectory() { + setState(() { + _tempDirectory = getTemporaryDirectory(); + }); + } + + Widget _buildDirectory( + BuildContext context, AsyncSnapshot snapshot) { + Text text = const Text(''); + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + text = Text('Error: ${snapshot.error}'); + } else if (snapshot.hasData) { + text = Text('path: ${snapshot.data.path}'); + } else { + text = const Text('path unavailable'); + } + } + return Padding(padding: const EdgeInsets.all(16.0), child: text); + } + + Widget _buildDirectories( + BuildContext context, AsyncSnapshot> snapshot) { + Text text = const Text(''); + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasError) { + text = Text('Error: ${snapshot.error}'); + } else if (snapshot.hasData) { + final String combined = + snapshot.data.map((Directory d) => d.path).join(', '); + text = Text('paths: $combined'); + } else { + text = const Text('path unavailable'); + } + } + return Padding(padding: const EdgeInsets.all(16.0), child: text); + } + + void _requestAppDocumentsDirectory() { + setState(() { + _appDocumentsDirectory = getApplicationDocumentsDirectory(); + }); + } + + void _requestAppDownloadsDirectory() { + setState(() { + _appDownloadsDirectory = getDownloadsDirectory(); + }); + } + + void _requestAppSupportDirectory() { + setState(() { + _appSupportDirectory = getApplicationSupportDirectory(); + }); + } + + void _requestAppLibraryDirectory() { + setState(() { + _appLibraryDirectory = getLibraryDirectory(); + }); + } + + void _requestExternalStorageDirectory() { + setState(() { + _externalDocumentsDirectory = getExternalStorageDirectory(); + }); + } + + void _requestExternalStorageDirectories(StorageDirectory type) { + setState(() { + _externalStorageDirectories = getExternalStorageDirectories(type: type); + }); + } + + void _requestExternalCacheDirectories() { + setState(() { + _externalCacheDirectories = getExternalCacheDirectories(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: const Text('Get Application Documents Directory'), + onPressed: _requestAppDocumentsDirectory, + ), + ), + FutureBuilder( + future: _appDocumentsDirectory, builder: _buildDirectory), + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: const Text('Get Application Downloads Directory'), + onPressed: _requestAppDownloadsDirectory, + ), + ), + FutureBuilder( + future: _appDownloadsDirectory, builder: _buildDirectory), + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: const Text('Get Temporary Directory'), + onPressed: _requestTempDirectory, + ), + ), + FutureBuilder( + future: _tempDirectory, builder: _buildDirectory), + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: const Text('Get Application Support Directory'), + onPressed: _requestAppSupportDirectory, + ), + ), + FutureBuilder( + future: _appSupportDirectory, builder: _buildDirectory), + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: const Text('Get Application Library Directory'), + onPressed: _requestAppLibraryDirectory, + ), + ), + FutureBuilder( + future: _appLibraryDirectory, builder: _buildDirectory), + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: Text( + '${Platform.isIOS ? "External directories are unavailable " "on iOS" : "Get External Storage Directory"}'), + onPressed: + Platform.isIOS ? null : _requestExternalStorageDirectory, + ), + ), + FutureBuilder( + future: _externalDocumentsDirectory, builder: _buildDirectory), + Column(children: [ + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: Text( + '${Platform.isIOS ? "External directories are unavailable " "on iOS" : "Get External Storage Directories"}'), + onPressed: Platform.isIOS + ? null + : () { + _requestExternalStorageDirectories( + StorageDirectory.music, + ); + }, + ), + ), + ]), + FutureBuilder>( + future: _externalStorageDirectories, + builder: _buildDirectories), + Column(children: [ + Padding( + padding: const EdgeInsets.all(6.0), + child: RaisedButton( + child: Text( + '${Platform.isIOS ? "External directories are unavailable " "on iOS" : "Get External Cache Directories"}'), + onPressed: + Platform.isIOS ? null : _requestExternalCacheDirectories, + ), + ), + ]), + FutureBuilder>( + future: _externalCacheDirectories, builder: _buildDirectories), + ], + ), + ), + ); + } +} diff --git a/packages/path_provider/example/pubspec.yaml b/packages/path_provider/example/pubspec.yaml new file mode 100644 index 000000000..734e00484 --- /dev/null +++ b/packages/path_provider/example/pubspec.yaml @@ -0,0 +1,29 @@ +name: path_provider_tizen_example +description: Demonstrates how to use the path_provider_tizen plugin. +publish_to: 'none' + +dependencies: + flutter: + sdk: flutter + path_provider: ^1.6.10 + path_provider_tizen: + path: ../ + +dev_dependencies: + integration_test: ^0.9.2 + integration_test_tizen: + path: ../../integration_test/ + permission_handler: ^5.0.1+1 + # Remove when testing on TV. + permission_handler_tizen: + path: ../../permission_handler/ + flutter_driver: + sdk: flutter + pedantic: ^1.8.0 + +flutter: + uses-material-design: true + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/packages/path_provider/example/test_driver/integration_test.dart b/packages/path_provider/example/test_driver/integration_test.dart new file mode 100644 index 000000000..b38629cca --- /dev/null +++ b/packages/path_provider/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/path_provider/example/tizen/.gitignore b/packages/path_provider/example/tizen/.gitignore new file mode 100644 index 000000000..750f3af1b --- /dev/null +++ b/packages/path_provider/example/tizen/.gitignore @@ -0,0 +1,5 @@ +flutter/ +.vs/ +*.user +bin/ +obj/ diff --git a/packages/path_provider/example/tizen/App.cs b/packages/path_provider/example/tizen/App.cs new file mode 100644 index 000000000..6dd4a6356 --- /dev/null +++ b/packages/path_provider/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/path_provider/example/tizen/NuGet.Config b/packages/path_provider/example/tizen/NuGet.Config new file mode 100644 index 000000000..c4ea70c17 --- /dev/null +++ b/packages/path_provider/example/tizen/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/path_provider/example/tizen/Runner.csproj b/packages/path_provider/example/tizen/Runner.csproj new file mode 100644 index 000000000..8ebc2abdf --- /dev/null +++ b/packages/path_provider/example/tizen/Runner.csproj @@ -0,0 +1,26 @@ + + + + Exe + tizen60 + + + + portable + + + none + + + + + + + + + + %(RecursiveDir) + + + + diff --git a/packages/path_provider/example/tizen/shared/res/ic_launcher.png b/packages/path_provider/example/tizen/shared/res/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/packages/path_provider/example/tizen/shared/res/ic_launcher.png differ diff --git a/packages/path_provider/example/tizen/tizen-manifest.xml b/packages/path_provider/example/tizen/tizen-manifest.xml new file mode 100644 index 000000000..291566a3d --- /dev/null +++ b/packages/path_provider/example/tizen/tizen-manifest.xml @@ -0,0 +1,15 @@ + + + + + + ic_launcher.png + + + + + + http://tizen.org/privilege/mediastorage + http://tizen.org/privilege/externalstorage.appdata + + diff --git a/packages/path_provider/lib/path_provider_tizen.dart b/packages/path_provider/lib/path_provider_tizen.dart new file mode 100644 index 000000000..54895835f --- /dev/null +++ b/packages/path_provider/lib/path_provider_tizen.dart @@ -0,0 +1,93 @@ +// Copyright 2020 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:path_provider_platform_interface/path_provider_platform_interface.dart'; + +import 'src/app_common.dart'; +import 'src/storage.dart'; + +/// The Tizen implementation of [PathProviderPlatform] +/// +/// This class implements the `package:path_provider` functionality for Tizen +class PathProviderPlugin extends PathProviderPlatform { + /// Registers this class as the default instance of [PathProviderPlatform] + static void register() { + PathProviderPlatform.instance = PathProviderPlugin(); + } + + @override + Future getTemporaryPath() async { + assert(appCommon != null); + return appCommon.getCachePath(); + } + + @override + Future getApplicationDocumentsPath() async { + assert(appCommon != null); + return appCommon.getDataPath(); + } + + @override + Future getApplicationSupportPath() async { + assert(appCommon != null); + return appCommon.getDataPath(); + } + + @override + Future getExternalStoragePath() async { + assert(appCommon != null); + return appCommon.getExternalDataPath(); + } + + @override + Future> getExternalCachePaths() async { + assert(appCommon != null); + return [appCommon.getExternalCachePath()]; + } + + @override + Future> getExternalStoragePaths({ + StorageDirectory type, + }) async { + assert(storage != null); + StorageDirectoryType dirType; + switch (type) { + case StorageDirectory.music: + dirType = StorageDirectoryType.music; + break; + case StorageDirectory.ringtones: + dirType = StorageDirectoryType.system_ringtones; + break; + case StorageDirectory.pictures: + dirType = StorageDirectoryType.images; + break; + case StorageDirectory.movies: + dirType = StorageDirectoryType.videos; + break; + case StorageDirectory.downloads: + dirType = StorageDirectoryType.downloads; + break; + case StorageDirectory.dcim: + dirType = StorageDirectoryType.camera; + break; + case StorageDirectory.documents: + dirType = StorageDirectoryType.documents; + break; + case StorageDirectory.podcasts: + case StorageDirectory.alarms: + case StorageDirectory.notifications: + default: + dirType = StorageDirectoryType.others; + break; + } + return [await storage.getDirectory(type: dirType)]; + } + + @override + Future getDownloadsPath() async { + assert(storage != null); + return await storage.getDirectory(type: StorageDirectoryType.downloads); + } +} diff --git a/packages/path_provider/lib/src/app_common.dart b/packages/path_provider/lib/src/app_common.dart new file mode 100644 index 000000000..6d14367af --- /dev/null +++ b/packages/path_provider/lib/src/app_common.dart @@ -0,0 +1,70 @@ +// Copyright 2020 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:ffi'; +import 'package:ffi/ffi.dart'; + +typedef app_common_get_data_path = Pointer Function(); +typedef app_common_get_cache_path = Pointer Function(); +typedef app_common_get_external_data_path = Pointer Function(); +typedef app_common_get_external_cache_path = Pointer Function(); + +AppCommon _appCommonInstance; +AppCommon get appCommon => _appCommonInstance ??= AppCommon(); + +/// A wrapper class for Tizen App Common APIs. +/// Not all functions or values are supported. +class AppCommon { + AppCommon() { + final DynamicLibrary libAppCommon = + DynamicLibrary.open('libcapi-appfw-app-common.so.0'); + _getDataPath = libAppCommon + .lookup>('app_get_data_path') + .asFunction(); + _getCachePath = libAppCommon + .lookup>('app_get_cache_path') + .asFunction(); + _getExternalDataPath = libAppCommon + .lookup>( + 'app_get_external_data_path') + .asFunction(); + _getExternalCachePath = libAppCommon + .lookup>( + 'app_get_external_cache_path') + .asFunction(); + } + + Pointer Function() _getDataPath; + Pointer Function() _getCachePath; + Pointer Function() _getExternalDataPath; + Pointer Function() _getExternalCachePath; + + String getCachePath() { + final Pointer path = _getCachePath(); + final String str = (path != null) ? Utf8.fromUtf8(path) : ''; + free(path); + return str; + } + + String getDataPath() { + final Pointer path = _getDataPath(); + final String str = (path != null) ? Utf8.fromUtf8(path) : ''; + free(path); + return str; + } + + String getExternalDataPath() { + final Pointer path = _getExternalDataPath(); + final String str = (path != null) ? Utf8.fromUtf8(path) : ''; + free(path); + return str; + } + + String getExternalCachePath() { + final Pointer path = _getExternalCachePath(); + final String str = (path != null) ? Utf8.fromUtf8(path) : ''; + free(path); + return str; + } +} diff --git a/packages/path_provider/lib/src/storage.dart b/packages/path_provider/lib/src/storage.dart new file mode 100644 index 000000000..f8ddc4ec3 --- /dev/null +++ b/packages/path_provider/lib/src/storage.dart @@ -0,0 +1,110 @@ +// Copyright 2020 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 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +// Native function signatures +typedef storage_get_directory = Int32 Function( + Int32, Int32, Pointer>); +typedef _storage_callback = Int32 Function( + Int32, Int32, Int32, Pointer, Pointer); +typedef storage_foreach_device_supported = Int32 Function( + Pointer>, Pointer); + +enum StorageDirectoryType { + images, + sounds, + videos, + camera, + downloads, + music, + documents, + others, + system_ringtones, +} + +class StorageException { + StorageException(this.returnCode, this.message); + + final int returnCode; + final String message; + + @override + String toString() => '$message ($returnCode)'; +} + +Storage _storageInstance; +Storage get storage => _storageInstance ??= Storage(); + +/// A wrapper class for Tizen Storage APIs. +/// Not all functions or values are supported. +class Storage { + Storage() { + final DynamicLibrary libStorage = DynamicLibrary.open('libstorage.so.0.1'); + _storageGetDirectory = libStorage + .lookup>('storage_get_directory') + .asFunction(); + + _storageForeachDeviceSupported = libStorage + .lookup>( + 'storage_foreach_device_supported') + .asFunction(); + + if (_completer.isCompleted) { + return; + } + + final int ret = _storageForeachDeviceSupported( + Pointer.fromFunction(_deviceSupportedCallback, 0), nullptr); + if (ret != 0) { + throw StorageException( + ret, 'Failed to execute storage_foreach_device_supported.'); + } + } + + // Bindings + int Function(int, int, Pointer>) _storageGetDirectory; + int Function(Pointer>, Pointer) + _storageForeachDeviceSupported; + + /// The unique storage device id. + final Future storageId = _completer.future; + + /// A completer for [storageId]. + static final Completer _completer = Completer(); + + static int _deviceSupportedCallback( + int storageId, + int type, + int state, + Pointer path, + Pointer userData, + ) { + // internal storage + if (type == 0) { + _completer.complete(storageId); + return 0; + } + return 1; + } + + Future getDirectory({ + StorageDirectoryType type, + }) async { + final Pointer> path = allocate(); + try { + final int result = + _storageGetDirectory(await storageId, type.index, path); + if (result != 0) { + throw StorageException( + result, 'Failed to execute storage_get_directory.'); + } + return Utf8.fromUtf8(path.value); + } finally { + free(path); + } + } +} diff --git a/packages/path_provider/pubspec.yaml b/packages/path_provider/pubspec.yaml new file mode 100644 index 000000000..21358b255 --- /dev/null +++ b/packages/path_provider/pubspec.yaml @@ -0,0 +1,26 @@ +name: path_provider_tizen +description: Tizen implementation of the path_provider plugin +version: 1.0.0 +homepage: https://github.com/flutter-tizen/plugins + +flutter: + plugin: + platforms: + tizen: + dartPluginClass: PathProviderPlugin + fileName: path_provider_tizen.dart + +dependencies: + flutter: + sdk: flutter + path_provider_platform_interface: ^1.0.1 + ffi: ^0.1.3 + +dev_dependencies: + flutter_test: + sdk: flutter + pedantic: ^1.8.0 + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0"