From dc5f8b997ba9ef4e9a02aea55b64898a20c8c1f8 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 16 Feb 2021 15:58:19 +1300 Subject: [PATCH 1/4] [path_provider] Use the application ID in the application support path (#2845) Use the existing executable named directory if it exists, to allow backwards compatibility to work. --- .../path_provider_linux/CHANGELOG.md | 5 + .../lib/path_provider_linux.dart | 59 +---------- .../lib/src/get_application_id.dart | 9 ++ .../lib/src/get_application_id_real.dart | 42 ++++++++ .../lib/src/get_application_id_stub.dart | 6 ++ .../lib/src/path_provider_linux.dart | 98 +++++++++++++++++++ .../path_provider_linux/pubspec.yaml | 3 +- .../test/path_provider_linux_test.dart | 16 ++- 8 files changed, 177 insertions(+), 61 deletions(-) create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart create mode 100644 packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index 6f18d0d6ae58..4064897d48bf 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Change getApplicationSupportPath from using executable name to application ID (if provided). + * If the executable name based directory exists, continue to use that so existing applications continue with the same behaviour. + ## 2.1.0 * Now `getTemporaryPath` returns the value of the `TMPDIR` environment variable primarily. If `TMPDIR` is not set, `/tmp` is returned. diff --git a/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart index ab18db69ddfb..e32af1bf5f13 100644 --- a/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart +++ b/packages/path_provider/path_provider_linux/lib/path_provider_linux.dart @@ -2,61 +2,4 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:path/path.dart' as path; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; -import 'package:xdg_directories/xdg_directories.dart' as xdg; - -/// The linux implementation of [PathProviderPlatform] -/// -/// This class implements the `package:path_provider` functionality for linux -class PathProviderLinux extends PathProviderPlatform { - /// Constructs an instance of [PathProviderLinux] - PathProviderLinux() : _environment = Platform.environment; - - /// Constructs an instance of [PathProviderLinux] with the given [environment] - @visibleForTesting - PathProviderLinux.private({ - required Map environment, - }) : _environment = environment; - - final Map _environment; - - /// Registers this class as the default instance of [PathProviderPlatform] - static void registerWith() { - PathProviderPlatform.instance = PathProviderLinux(); - } - - @override - Future getTemporaryPath() { - final String environmentTmpDir = _environment['TMPDIR'] ?? ''; - return Future.value( - environmentTmpDir.isEmpty ? '/tmp' : environmentTmpDir, - ); - } - - @override - Future getApplicationSupportPath() async { - final String processName = path.basenameWithoutExtension( - await File('/proc/self/exe').resolveSymbolicLinks()); - final Directory directory = - Directory(path.join(xdg.dataHome.path, processName)); - // Creating the directory if it doesn't exist, because mobile implementations assume the directory exists - if (!directory.existsSync()) { - await directory.create(recursive: true); - } - return directory.path; - } - - @override - Future getApplicationDocumentsPath() { - return Future.value(xdg.getUserDirectory('DOCUMENTS')?.path); - } - - @override - Future getDownloadsPath() { - return Future.value(xdg.getUserDirectory('DOWNLOAD')?.path); - } -} +export 'src/path_provider_linux.dart'; diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart new file mode 100644 index 000000000000..e169c025eef1 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id.dart @@ -0,0 +1,9 @@ +// Copyright 2013 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. + +// getApplicationId() is implemented using FFI; export a stub for platforms +// that don't support FFI (e.g., web) to avoid having transitive dependencies +// break web compilation. +export 'get_application_id_stub.dart' + if (dart.library.ffi) 'get_application_id_real.dart'; diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart new file mode 100644 index 000000000000..f6d25bbe783e --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_real.dart @@ -0,0 +1,42 @@ +// Copyright 2013 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. + +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +// GApplication* g_application_get_default(); +typedef _GApplicationGetDefaultC = IntPtr Function(); +typedef _GApplicationGetDefaultDart = int Function(); + +// const gchar* g_application_get_application_id(GApplication* application); +typedef _GApplicationGetApplicationIdC = Pointer Function(IntPtr); +typedef _GApplicationGetApplicationIdDart = Pointer Function(int); + +/// Gets the application ID for this app. +String? getApplicationId() { + DynamicLibrary gio; + try { + gio = DynamicLibrary.open('libgio-2.0.so'); + } on ArgumentError { + return null; + } + final _GApplicationGetDefaultDart gApplicationGetDefault = + gio.lookupFunction<_GApplicationGetDefaultC, _GApplicationGetDefaultDart>( + 'g_application_get_default'); + final int app = gApplicationGetDefault(); + if (app == 0) { + return null; + } + + final _GApplicationGetApplicationIdDart gApplicationGetApplicationId = + gio.lookupFunction<_GApplicationGetApplicationIdC, + _GApplicationGetApplicationIdDart>( + 'g_application_get_application_id'); + final Pointer appId = gApplicationGetApplicationId(app); + if (appId == null) { + return null; + } + + return appId.toDartString(); +} diff --git a/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart b/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart new file mode 100644 index 000000000000..909997693626 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/get_application_id_stub.dart @@ -0,0 +1,6 @@ +// Copyright 2013 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. + +/// Gets the application ID for this app. +String? getApplicationId() => null; diff --git a/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart new file mode 100644 index 000000000000..e1953c81aa90 --- /dev/null +++ b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart @@ -0,0 +1,98 @@ +// Copyright 2013 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. + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:xdg_directories/xdg_directories.dart' as xdg; + +import 'get_application_id.dart'; + +/// The linux implementation of [PathProviderPlatform] +/// +/// This class implements the `package:path_provider` functionality for Linux. +class PathProviderLinux extends PathProviderPlatform { + /// Constructs an instance of [PathProviderLinux] + PathProviderLinux() : _environment = Platform.environment; + + /// Constructs an instance of [PathProviderLinux] with the given [environment] + @visibleForTesting + PathProviderLinux.private( + {Map environment = const {}, + String? executableName, + String? applicationId}) + : _environment = environment, + _executableName = executableName, + _applicationId = applicationId; + + final Map _environment; + String? _executableName; + String? _applicationId; + + /// Registers this class as the default instance of [PathProviderPlatform] + static void registerWith() { + PathProviderPlatform.instance = PathProviderLinux(); + } + + @override + Future getTemporaryPath() { + final String environmentTmpDir = _environment['TMPDIR'] ?? ''; + return Future.value( + environmentTmpDir.isEmpty ? '/tmp' : environmentTmpDir, + ); + } + + @override + Future getApplicationSupportPath() async { + final Directory directory = + Directory(path.join(xdg.dataHome.path, await _getId())); + if (directory.existsSync()) { + return directory.path; + } + + // This plugin originally used the executable name as a directory. + // Use that if it exists for backwards compatibility. + final Directory legacyDirectory = + Directory(path.join(xdg.dataHome.path, await _getExecutableName())); + if (legacyDirectory.existsSync()) { + return legacyDirectory.path; + } + + // Create the directory, because mobile implementations assume the directory exists. + await directory.create(recursive: true); + return directory.path; + } + + @override + Future getApplicationDocumentsPath() { + return Future.value(xdg.getUserDirectory('DOCUMENTS')?.path); + } + + @override + Future getDownloadsPath() { + return Future.value(xdg.getUserDirectory('DOWNLOAD')?.path); + } + + // Gets the name of this executable. + Future _getExecutableName() async { + _executableName ??= path.basenameWithoutExtension( + await File('/proc/self/exe').resolveSymbolicLinks()); + return _executableName!; + } + + // Gets the unique ID for this application. + Future _getId() async { + _applicationId ??= getApplicationId(); + + if (_applicationId != null) { + return _applicationId!; + } + + // Fall back to using the executable name. + return await _getExecutableName(); + } +} diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index f5b7a88ca232..197e9c811422 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -17,6 +17,7 @@ flutter: pluginClass: none dependencies: + ffi: ^1.1.2 flutter: sdk: flutter path: ^1.8.0 diff --git a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart index 6dd35000a8ea..743ba6287c9c 100644 --- a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart +++ b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:xdg_directories/xdg_directories.dart' as xdg; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -35,8 +36,19 @@ void main() { }); test('getApplicationSupportPath', () async { - final PathProviderPlatform plugin = PathProviderPlatform.instance; - expect(await plugin.getApplicationSupportPath(), startsWith('/')); + final PathProviderPlatform plugin = PathProviderLinux.private( + executableName: 'test', applicationId: 'com.example.Test'); + // Note this will fail if ${xdg.dataHome.path}/test exists on the local filesystem. + expect(await plugin.getApplicationSupportPath(), + '${xdg.dataHome.path}/com.example.Test'); + }); + + test('getApplicationSupportPath uses executable name if no application Id', + () async { + final PathProviderPlatform plugin = + PathProviderLinux.private(executableName: 'test'); + expect( + await plugin.getApplicationSupportPath(), '${xdg.dataHome.path}/test'); }); test('getApplicationDocumentsPath', () async { From 0f8b0bbcb143624644541c713cd619d2fcc4cf84 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 6 Dec 2021 11:49:57 +1300 Subject: [PATCH 2/4] Remove unused import --- .../path_provider_linux/lib/src/path_provider_linux.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart index e1953c81aa90..d3fe794b019c 100644 --- a/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart +++ b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart @@ -2,7 +2,6 @@ // 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/foundation.dart'; From 40360d534ff601c819e3d33195d07e1e92813d95 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 6 Dec 2021 11:51:58 +1300 Subject: [PATCH 3/4] Use a binary name less likely to collide with another test / real world binary name --- .../test/path_provider_linux_test.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart index 743ba6287c9c..1f567c00513d 100644 --- a/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart +++ b/packages/path_provider/path_provider_linux/test/path_provider_linux_test.dart @@ -37,18 +37,19 @@ void main() { test('getApplicationSupportPath', () async { final PathProviderPlatform plugin = PathProviderLinux.private( - executableName: 'test', applicationId: 'com.example.Test'); - // Note this will fail if ${xdg.dataHome.path}/test exists on the local filesystem. + executableName: 'path_provider_linux_test_binary', + applicationId: 'com.example.Test'); + // Note this will fail if ${xdg.dataHome.path}/path_provider_linux_test_binary exists on the local filesystem. expect(await plugin.getApplicationSupportPath(), '${xdg.dataHome.path}/com.example.Test'); }); test('getApplicationSupportPath uses executable name if no application Id', () async { - final PathProviderPlatform plugin = - PathProviderLinux.private(executableName: 'test'); - expect( - await plugin.getApplicationSupportPath(), '${xdg.dataHome.path}/test'); + final PathProviderPlatform plugin = PathProviderLinux.private( + executableName: 'path_provider_linux_test_binary'); + expect(await plugin.getApplicationSupportPath(), + '${xdg.dataHome.path}/path_provider_linux_test_binary'); }); test('getApplicationDocumentsPath', () async { From 2c132a7e328dac8d91242010d3bb8e3946192069 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 6 Dec 2021 11:53:49 +1300 Subject: [PATCH 4/4] Simplify ID fallback case --- .../path_provider_linux/lib/src/path_provider_linux.dart | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart index d3fe794b019c..1544dcea2984 100644 --- a/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart +++ b/packages/path_provider/path_provider_linux/lib/src/path_provider_linux.dart @@ -86,12 +86,7 @@ class PathProviderLinux extends PathProviderPlatform { // Gets the unique ID for this application. Future _getId() async { _applicationId ??= getApplicationId(); - - if (_applicationId != null) { - return _applicationId!; - } - - // Fall back to using the executable name. - return await _getExecutableName(); + // If no application ID then fall back to using the executable name. + return _applicationId ?? await _getExecutableName(); } }