From 870c361915d36fb02ee0ebe916fa1139fd7a057e Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 29 Apr 2016 12:15:04 -0700 Subject: [PATCH] Add ergonomic wrappers for Flutter platform services (#3636) These wrappers also give us a chance to document what these services do. Fixes #3164 --- packages/flutter/lib/services.dart | 6 +- .../flutter/lib/src/material/date_picker.dart | 6 +- .../flutter/lib/src/material/time_picker.dart | 4 +- .../flutter/lib/src/services/activity.dart | 28 +-------- .../lib/src/services/app_messages.dart | 18 +++--- .../lib/src/services/haptic_feedback.dart | 44 +++++++++++++ .../lib/src/services/path_provider.dart | 49 +++++++++++++++ .../lib/src/services/system_chrome.dart | 62 +++++++++++++++++++ .../lib/src/services/system_sound.dart | 37 +++++++++++ .../flutter/lib/src/widgets/drag_target.dart | 2 +- 10 files changed, 215 insertions(+), 41 deletions(-) create mode 100644 packages/flutter/lib/src/services/haptic_feedback.dart create mode 100644 packages/flutter/lib/src/services/path_provider.dart create mode 100644 packages/flutter/lib/src/services/system_chrome.dart create mode 100644 packages/flutter/lib/src/services/system_sound.dart diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index 73e1205088..149c4a0edc 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. /// System services exposed to Flutter apps. -/// +/// /// To use, import `package:flutter/services.dart`. /// /// For example, this library includes [fetch], which fetches data from the @@ -18,8 +18,12 @@ export 'src/services/activity.dart'; export 'src/services/app_messages.dart'; export 'src/services/asset_bundle.dart'; export 'src/services/binding.dart'; +export 'src/services/haptic_feedback.dart'; export 'src/services/image_cache.dart'; export 'src/services/image_decoder.dart'; export 'src/services/image_resource.dart'; export 'src/services/keyboard.dart'; +export 'src/services/path_provider.dart'; export 'src/services/shell.dart'; +export 'src/services/system_chrome.dart'; +export 'src/services/system_sound.dart'; diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index 9328997c4a..cdaa8ad1c4 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -66,14 +66,14 @@ class _DatePickerState extends State { _DatePickerMode _mode = _DatePickerMode.day; void _handleModeChanged(_DatePickerMode mode) { - userFeedback.performHapticFeedback(HapticFeedbackType.virtualKey); + HapticFeedback.vibrate(); setState(() { _mode = mode; }); } void _handleYearChanged(DateTime dateTime) { - userFeedback.performHapticFeedback(HapticFeedbackType.virtualKey); + HapticFeedback.vibrate(); setState(() { _mode = _DatePickerMode.day; }); @@ -82,7 +82,7 @@ class _DatePickerState extends State { } void _handleDayChanged(DateTime dateTime) { - userFeedback.performHapticFeedback(HapticFeedbackType.virtualKey); + HapticFeedback.vibrate(); if (config.onChanged != null) config.onChanged(dateTime); } diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index ef63172bf9..119a98e01d 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -4,7 +4,7 @@ import 'dart:math' as math; -import 'package:flutter/services.dart' show HapticFeedbackType, userFeedback; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'colors.dart'; @@ -129,7 +129,7 @@ class _TimePickerState extends State { _TimePickerMode _mode = _TimePickerMode.hour; void _handleModeChanged(_TimePickerMode mode) { - userFeedback.performHapticFeedback(HapticFeedbackType.virtualKey); + HapticFeedback.vibrate(); setState(() { _mode = mode; }); diff --git a/packages/flutter/lib/src/services/activity.dart b/packages/flutter/lib/src/services/activity.dart index c5f603c84a..8980ccc18a 100644 --- a/packages/flutter/lib/src/services/activity.dart +++ b/packages/flutter/lib/src/services/activity.dart @@ -3,13 +3,13 @@ // found in the LICENSE file. import 'dart:ui'; -import 'dart:async'; import 'package:sky_services/activity/activity.mojom.dart'; import 'shell.dart'; -export 'package:sky_services/activity/activity.mojom.dart'; +export 'package:sky_services/activity/activity.mojom.dart' show Activity, Intent, ComponentName, StringExtra; + // Dart wrapper around Activity mojo service available in Flutter on Android. // @@ -21,7 +21,7 @@ export 'package:sky_services/activity/activity.mojom.dart'; /// Open a document into a new task rooted at the activity launched by /// this Intent. -/// +/// /// See Android's Intent.FLAG_ACTIVITY_NEW_DOCUMENT. const int NEW_DOCUMENT = 0x00080000; // ignore: constant_identifier_names @@ -44,24 +44,6 @@ ActivityProxy _initActivityProxy() { final ActivityProxy _activityProxy = _initActivityProxy(); final Activity activity = _activityProxy.ptr; -UserFeedbackProxy _initUserFeedbackProxy() { - UserFeedbackProxy proxy = new UserFeedbackProxy.unbound(); - _activityProxy.ptr.getUserFeedback(proxy); - return proxy; -} - -final UserFeedbackProxy _userFeedbackProxy = _initUserFeedbackProxy(); -final UserFeedback userFeedback = _userFeedbackProxy.ptr; - -PathServiceProxy _initPathServiceProxy() { - PathServiceProxy proxy = new PathServiceProxy.unbound(); - shell.connectToService(null, proxy); - return proxy; -} - -final PathServiceProxy _pathServiceProxy = _initPathServiceProxy(); -final PathService pathService = _pathServiceProxy.ptr; - Color _cachedPrimaryColor; String _cachedLabel; @@ -81,7 +63,3 @@ void updateTaskDescription({ String label, Color color }) { _activityProxy.ptr.setTaskDescription(description); } - -Future getAppDataDir() async => (await _pathServiceProxy.ptr.getAppDataDir()).path; -Future getFilesDir() async => (await _pathServiceProxy.ptr.getFilesDir()).path; -Future getCacheDir() async => (await _pathServiceProxy.ptr.getCacheDir()).path; diff --git a/packages/flutter/lib/src/services/app_messages.dart b/packages/flutter/lib/src/services/app_messages.dart index b3e8999b30..3c453ad1f1 100644 --- a/packages/flutter/lib/src/services/app_messages.dart +++ b/packages/flutter/lib/src/services/app_messages.dart @@ -5,30 +5,28 @@ import 'dart:async'; import 'package:mojo/core.dart' as core; -import 'package:sky_services/flutter/platform/app_messages.mojom.dart'; +import 'package:sky_services/flutter/platform/app_messages.mojom.dart' as mojom; import 'shell.dart'; -// APIs for exchanging messages with the host application. - -ApplicationMessagesProxy _initHostAppMessagesProxy() { - ApplicationMessagesProxy proxy = new ApplicationMessagesProxy.unbound(); +mojom.ApplicationMessagesProxy _initHostAppMessagesProxy() { + mojom.ApplicationMessagesProxy proxy = new mojom.ApplicationMessagesProxy.unbound(); shell.connectToViewAssociatedService(proxy); return proxy; } -final ApplicationMessagesProxy _hostAppMessagesProxy = _initHostAppMessagesProxy(); +final mojom.ApplicationMessagesProxy _hostAppMessagesProxy = _initHostAppMessagesProxy(); typedef Future HostMessageCallback(String message); typedef Object _SendStringResponseFactory(String response); -class _ApplicationMessagesImpl extends ApplicationMessages { +class _ApplicationMessagesImpl extends mojom.ApplicationMessages { final Map handlers = {}; _ApplicationMessagesImpl() { - shell.provideService(ApplicationMessages.serviceName, + shell.provideService(mojom.ApplicationMessages.serviceName, (core.MojoMessagePipeEndpoint endpoint) { - ApplicationMessagesStub stub = new ApplicationMessagesStub.fromEndpoint(endpoint); + mojom.ApplicationMessagesStub stub = new mojom.ApplicationMessagesStub.fromEndpoint(endpoint); stub.impl = this; } ); @@ -46,6 +44,8 @@ class _ApplicationMessagesImpl extends ApplicationMessages { final _ApplicationMessagesImpl _appMessages = new _ApplicationMessagesImpl(); +/// A service that can be implemented by the host application and the +/// Flutter framework to exchange application-specific messages. class HostMessages { /// Send a message to the host application. static Future sendToHost(String messageName, String message) async { diff --git a/packages/flutter/lib/src/services/haptic_feedback.dart b/packages/flutter/lib/src/services/haptic_feedback.dart new file mode 100644 index 0000000000..37133e4bbd --- /dev/null +++ b/packages/flutter/lib/src/services/haptic_feedback.dart @@ -0,0 +1,44 @@ +// Copyright 2016 The Chromium 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 'package:sky_services/flutter/platform/haptic_feedback.mojom.dart' as mojom; + +import 'shell.dart'; + +mojom.HapticFeedbackProxy _initHapticFeedbackProxy() { + mojom.HapticFeedbackProxy proxy = new mojom.HapticFeedbackProxy.unbound(); + shell.connectToViewAssociatedService(proxy); + return proxy; +} + +final mojom.HapticFeedbackProxy _hapticFeedbackProxy = _initHapticFeedbackProxy(); + +/// Allows access to the haptic feedback interface on the device. This API is +/// intentionally terse since it invokes default platform behavior. It is not +/// suitable for use if you require more flexible access to device sensors and +/// peripherals. +class HapticFeedback { + HapticFeedback._(); + + /// Provides haptic feedback to the user for a short duration. + /// + /// Platform Specific Notes: + /// + /// * _iOS_: Uses the platform "sound" for vibration (via + /// AudioServicesPlaySystemSound) + /// * _Android_: Uses the platform haptic feedback API that simulates a short + /// a short tap on a virtual keyboard. + /// + /// Return Value: + /// + /// boolean indicating if the intent to provide haptic feedback to the user + /// was successfully conveyed to the embedder. There may not be any actual + /// feedback if the device does not have a vibrator or one is disabled in + /// system settings. + static Future vibrate() async { + return (await _hapticFeedbackProxy.ptr.vibrate()).success; + } +} diff --git a/packages/flutter/lib/src/services/path_provider.dart b/packages/flutter/lib/src/services/path_provider.dart new file mode 100644 index 0000000000..ff38165ff9 --- /dev/null +++ b/packages/flutter/lib/src/services/path_provider.dart @@ -0,0 +1,49 @@ +// Copyright 2016 The Chromium 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:sky_services/flutter/platform/path_provider.mojom.dart' as mojom; + +import 'shell.dart'; + +mojom.PathProviderProxy _initPathProviderProxy() { + mojom.PathProviderProxy proxy = new mojom.PathProviderProxy.unbound(); + shell.connectToViewAssociatedService(proxy); + return proxy; +} + +final mojom.PathProviderProxy _pathProviderProxy = _initPathProviderProxy(); + +/// Returns commonly used locations on the filesystem. +class PathProvider { + PathProvider._(); + + /// Path to the temporary directory on the device. Files in this directory + /// may be cleared at any time. This does *not* return a new temporary + /// directory. Instead, the caller is responsible for creating + /// (and cleaning up) files or directories within this directory. This + /// directory is scoped to the calling application. + /// + /// Examples: + /// + /// * _iOS_: `NSTemporaryDirectory()` + /// * _Android_: `getCacheDir()` on the context. + static Future getTemporaryDirectory() async { + return new Directory((await _pathProviderProxy.ptr.temporaryDirectory()).path); + } + + /// Path to a directory where the application may place files that are private + /// to the application and will only be cleared when the application itself + /// is deleted. + /// + /// Examples: + /// + /// * iOS: `NSDocumentsDirectory` + /// * Android: The AppData directory. + static Future getApplicationDocumentsDirectory() async { + return new Directory((await _pathProviderProxy.ptr.applicationDocumentsDirectory()).path); + } +} diff --git a/packages/flutter/lib/src/services/system_chrome.dart b/packages/flutter/lib/src/services/system_chrome.dart new file mode 100644 index 0000000000..364668fc38 --- /dev/null +++ b/packages/flutter/lib/src/services/system_chrome.dart @@ -0,0 +1,62 @@ +// Copyright 2016 The Chromium 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 'package:sky_services/flutter/platform/system_chrome.mojom.dart' as mojom; + +import 'shell.dart'; + +export 'package:sky_services/flutter/platform/system_chrome.mojom.dart' show DeviceOrientation, SystemUIOverlay; + +mojom.SystemChromeProxy _initSystemChromeProxy() { + mojom.SystemChromeProxy proxy = new mojom.SystemChromeProxy.unbound(); + shell.connectToViewAssociatedService(proxy); + return proxy; +} + +final mojom.SystemChromeProxy _systemChromeProxy = _initSystemChromeProxy(); + +/// Controls specific aspects of the embedder interface. +class SystemChrome { + SystemChrome._(); + + /// Specifies the set of orientations the application interface can + /// be displayed in. + /// + /// Arguments: + /// + /// * `device_orientation_mask`: A mask of `DeviceOrientation` enum values. + /// The value 0 is synonymous with having all options enabled. + /// + /// Return Value: + /// + /// boolean indicating if the orientation mask is valid and the changes + /// could be conveyed successfully to the embedder. + static Future setPreferredOrientations(int deviceOrientationMask) async { + return (await _systemChromeProxy.ptr.setPreferredOrientations(deviceOrientationMask)).success; + } + + /// Specifies the set of overlays visible on the embedder when the + /// application is running. The embedder may choose to ignore unsupported + /// overlays + /// + /// Arguments: + /// + /// * `style`: A mask of `SystemUIOverlay` enum values that denotes the overlays + /// to show. + /// + /// Return Value: + /// + /// boolean indicating if the preference was conveyed successfully to the + /// embedder. + /// + /// Platform Specific Notes: + /// + /// If the overlay is unsupported on the platform, enabling or disabling + /// that overlay is a no-op and always return true. + static Future setEnabledSystemUIOverlays(int overlaysMask) async { + return (await _systemChromeProxy.ptr.setEnabledSystemUiOverlays(overlaysMask)).success; + } +} diff --git a/packages/flutter/lib/src/services/system_sound.dart b/packages/flutter/lib/src/services/system_sound.dart new file mode 100644 index 0000000000..e8ca8ad7c2 --- /dev/null +++ b/packages/flutter/lib/src/services/system_sound.dart @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium 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 'package:sky_services/flutter/platform/system_sound.mojom.dart' as mojom; + +import 'shell.dart'; + +export 'package:sky_services/flutter/platform/system_sound.mojom.dart' show SystemSoundType; + +mojom.SystemSoundProxy _initSystemSoundProxy() { + mojom.SystemSoundProxy proxy = new mojom.SystemSoundProxy.unbound(); + shell.connectToViewAssociatedService(proxy); + return proxy; +} + +final mojom.SystemSoundProxy _systemChromeProxy = _initSystemSoundProxy(); + +/// Allows easy access to the library of short system specific sounds for +/// common tasks. +class SystemSound { + SystemSound._(); + + /// Play the specified system sound. If that sound is not present on the + /// system, this method is a no-op and returns `true`. + /// + /// Return Value: + /// + /// boolean indicating if the intent to play the specified sound was + /// successfully conveyed to the embedder. No sound may actually play if the + /// device is muted or the sound was not available on the platform. + static Future play(mojom.SystemSoundType type) async { + return (await _systemChromeProxy.ptr.play(type)).success; + } +} diff --git a/packages/flutter/lib/src/widgets/drag_target.dart b/packages/flutter/lib/src/widgets/drag_target.dart index ccbc9956cf..57b7341283 100644 --- a/packages/flutter/lib/src/widgets/drag_target.dart +++ b/packages/flutter/lib/src/widgets/drag_target.dart @@ -217,7 +217,7 @@ class LongPressDraggable extends DraggableBase { ..onStart = (Point position) { Drag result = onStart(position); if (result != null) - userFeedback.performHapticFeedback(HapticFeedbackType.virtualKey); + HapticFeedback.vibrate(); return result; }; }