mirror of
https://github.com/flutter/packages.git
synced 2025-06-30 14:47:22 +08:00
[quick_actions] convert to pigeon (#5159)
Converts from direct use of method channels to using Pigeon. Part of https://github.com/flutter/flutter/issues/117844 fixes https://github.com/flutter/flutter/issues/117913 fixes https://github.com/flutter/flutter/issues/117112
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
## 1.0.8
|
||||
|
||||
* Changes method channels to pigeon.
|
||||
|
||||
## 1.0.7
|
||||
|
||||
* Adds pub topics to package metadata.
|
||||
|
@ -16,12 +16,9 @@
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
E092A7ED28D10802005C7F67 /* MockMethodChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7EA28D10801005C7F67 /* MockMethodChannel.swift */; };
|
||||
E092A7EE28D10802005C7F67 /* QuickActionsPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7EB28D10802005C7F67 /* QuickActionsPluginTests.swift */; };
|
||||
E092A7F128D10890005C7F67 /* MockShortcutItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F028D10890005C7F67 /* MockShortcutItemProvider.swift */; };
|
||||
E092A7F428D110B3005C7F67 /* DefaultShortcutItemParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F328D110B3005C7F67 /* DefaultShortcutItemParserTests.swift */; };
|
||||
E092A7F628D128EB005C7F67 /* RunnerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F528D128EB005C7F67 /* RunnerUITests.swift */; };
|
||||
E0A075D529147FE200329BAE /* MockShortcutItemParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0A075D429147FE200329BAE /* MockShortcutItemParser.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -78,12 +75,9 @@
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
C35AD3650AB6BF850E016715 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E092A7EA28D10801005C7F67 /* MockMethodChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockMethodChannel.swift; sourceTree = "<group>"; };
|
||||
E092A7EB28D10802005C7F67 /* QuickActionsPluginTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickActionsPluginTests.swift; sourceTree = "<group>"; };
|
||||
E092A7F028D10890005C7F67 /* MockShortcutItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockShortcutItemProvider.swift; sourceTree = "<group>"; };
|
||||
E092A7F328D110B3005C7F67 /* DefaultShortcutItemParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultShortcutItemParserTests.swift; sourceTree = "<group>"; };
|
||||
E092A7F528D128EB005C7F67 /* RunnerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerUITests.swift; sourceTree = "<group>"; };
|
||||
E0A075D429147FE200329BAE /* MockShortcutItemParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockShortcutItemParser.swift; sourceTree = "<group>"; };
|
||||
F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -120,7 +114,6 @@
|
||||
E092A7F228D10908005C7F67 /* Mocks */,
|
||||
33E20B3626EFCDFC00A4A191 /* Info.plist */,
|
||||
E092A7EB28D10802005C7F67 /* QuickActionsPluginTests.swift */,
|
||||
E092A7F328D110B3005C7F67 /* DefaultShortcutItemParserTests.swift */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
@ -215,9 +208,7 @@
|
||||
E092A7F228D10908005C7F67 /* Mocks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E092A7EA28D10801005C7F67 /* MockMethodChannel.swift */,
|
||||
E092A7F028D10890005C7F67 /* MockShortcutItemProvider.swift */,
|
||||
E0A075D429147FE200329BAE /* MockShortcutItemParser.swift */,
|
||||
);
|
||||
path = Mocks;
|
||||
sourceTree = "<group>";
|
||||
@ -289,7 +280,7 @@
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1300;
|
||||
LastUpgradeCheck = 1430;
|
||||
ORGANIZATIONNAME = "The Flutter Authors";
|
||||
TargetAttributes = {
|
||||
33E20B3126EFCDFC00A4A191 = {
|
||||
@ -364,6 +355,7 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
@ -435,10 +427,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E092A7EE28D10802005C7F67 /* QuickActionsPluginTests.swift in Sources */,
|
||||
E092A7ED28D10802005C7F67 /* MockMethodChannel.swift in Sources */,
|
||||
E092A7F128D10890005C7F67 /* MockShortcutItemProvider.swift in Sources */,
|
||||
E0A075D529147FE200329BAE /* MockShortcutItemParser.swift in Sources */,
|
||||
E092A7F428D110B3005C7F67 /* DefaultShortcutItemParserTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1300"
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,67 +0,0 @@
|
||||
// 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 Flutter
|
||||
import XCTest
|
||||
|
||||
@testable import quick_actions_ios
|
||||
|
||||
class DefaultShortcutItemParserTests: XCTestCase {
|
||||
|
||||
func testParseShortcutItems() {
|
||||
let rawItem = [
|
||||
"type": "SearchTheThing",
|
||||
"localizedTitle": "Search the thing",
|
||||
"icon": "search_the_thing.png",
|
||||
]
|
||||
|
||||
let expectedItem = UIApplicationShortcutItem(
|
||||
type: "SearchTheThing",
|
||||
localizedTitle: "Search the thing",
|
||||
localizedSubtitle: nil,
|
||||
icon: UIApplicationShortcutIcon(templateImageName: "search_the_thing.png"),
|
||||
userInfo: nil)
|
||||
|
||||
let parser = DefaultShortcutItemParser()
|
||||
XCTAssertEqual(parser.parseShortcutItems([rawItem]), [expectedItem])
|
||||
}
|
||||
|
||||
func testParseShortcutItems_noIcon() {
|
||||
let rawItem: [String: Any] = [
|
||||
"type": "SearchTheThing",
|
||||
"localizedTitle": "Search the thing",
|
||||
"icon": NSNull(),
|
||||
]
|
||||
|
||||
let expectedItem = UIApplicationShortcutItem(
|
||||
type: "SearchTheThing",
|
||||
localizedTitle: "Search the thing",
|
||||
localizedSubtitle: nil,
|
||||
icon: nil,
|
||||
userInfo: nil)
|
||||
|
||||
let parser = DefaultShortcutItemParser()
|
||||
XCTAssertEqual(parser.parseShortcutItems([rawItem]), [expectedItem])
|
||||
}
|
||||
|
||||
func testParseShortcutItems_noType() {
|
||||
let rawItem = [
|
||||
"localizedTitle": "Search the thing",
|
||||
"icon": "search_the_thing.png",
|
||||
]
|
||||
|
||||
let parser = DefaultShortcutItemParser()
|
||||
XCTAssertEqual(parser.parseShortcutItems([rawItem]), [])
|
||||
}
|
||||
|
||||
func testParseShortcutItems_noLocalizedTitle() {
|
||||
let rawItem = [
|
||||
"type": "SearchTheThing",
|
||||
"icon": "search_the_thing.png",
|
||||
]
|
||||
|
||||
let parser = DefaultShortcutItemParser()
|
||||
XCTAssertEqual(parser.parseShortcutItems([rawItem]), [])
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
// 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 Foundation
|
||||
|
||||
@testable import quick_actions_ios
|
||||
|
||||
final class MockMethodChannel: MethodChannel {
|
||||
var invokeMethodStub: ((_ methods: String, _ arguments: Any?) -> Void)? = nil
|
||||
func invokeMethod(_ method: String, arguments: Any?) {
|
||||
invokeMethodStub?(method, arguments)
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// 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 Foundation
|
||||
|
||||
@testable import quick_actions_ios
|
||||
|
||||
final class MockShortcutItemParser: ShortcutItemParser {
|
||||
|
||||
var parseShortcutItemsStub: ((_ items: [[String: Any]]) -> [UIApplicationShortcutItem])? = nil
|
||||
|
||||
func parseShortcutItems(_ items: [[String: Any]]) -> [UIApplicationShortcutItem] {
|
||||
return parseShortcutItemsStub?(items) ?? []
|
||||
}
|
||||
}
|
@ -7,14 +7,27 @@ import XCTest
|
||||
|
||||
@testable import quick_actions_ios
|
||||
|
||||
class MockFlutterApi: IOSQuickActionsFlutterApiProtocol {
|
||||
/// Method to allow for async testing.
|
||||
var launchActionCallback: ((String) -> Void)? = nil
|
||||
|
||||
func launchAction(
|
||||
action actionArg: String, completion: @escaping (Result<Void, FlutterError>) -> Void
|
||||
) {
|
||||
self.launchActionCallback?(actionArg)
|
||||
completion(.success(Void()))
|
||||
}
|
||||
}
|
||||
|
||||
class QuickActionsPluginTests: XCTestCase {
|
||||
|
||||
func testHandleMethodCall_setShortcutItems() {
|
||||
let rawItem = [
|
||||
"type": "SearchTheThing",
|
||||
"localizedTitle": "Search the thing",
|
||||
"icon": "search_the_thing.png",
|
||||
]
|
||||
let rawItem = ShortcutItemMessage(
|
||||
type: "SearchTheThing",
|
||||
localizedTitle: "Search the thing",
|
||||
icon: "search_the_thing.png"
|
||||
)
|
||||
|
||||
let item = UIApplicationShortcutItem(
|
||||
type: "SearchTheThing",
|
||||
localizedTitle: "Search the thing",
|
||||
@ -22,32 +35,15 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
icon: UIApplicationShortcutIcon(templateImageName: "search_the_thing.png"),
|
||||
userInfo: nil)
|
||||
|
||||
let call = FlutterMethodCall(methodName: "setShortcutItems", arguments: [rawItem])
|
||||
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
let parseShortcutItemsExpectation = expectation(
|
||||
description: "parseShortcutItems must be called.")
|
||||
mockShortcutItemParser.parseShortcutItemsStub = { items in
|
||||
XCTAssertEqual(items as? [[String: String]], [rawItem])
|
||||
parseShortcutItemsExpectation.fulfill()
|
||||
return [item]
|
||||
}
|
||||
|
||||
let resultExpectation = expectation(description: "result block must be called.")
|
||||
plugin.handle(call) { result in
|
||||
XCTAssertNil(result, "result block must be called with nil.")
|
||||
resultExpectation.fulfill()
|
||||
}
|
||||
plugin.setShortcutItems(itemsList: [rawItem])
|
||||
XCTAssertEqual(mockShortcutItemProvider.shortcutItems, [item], "Must set shortcut items.")
|
||||
waitForExpectations(timeout: 1)
|
||||
}
|
||||
|
||||
func testHandleMethodCall_clearShortcutItems() {
|
||||
@ -58,82 +54,28 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
icon: UIApplicationShortcutIcon(templateImageName: "search_the_thing.png"),
|
||||
userInfo: nil)
|
||||
|
||||
let call = FlutterMethodCall(methodName: "clearShortcutItems", arguments: nil)
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
mockShortcutItemProvider.shortcutItems = [item]
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
|
||||
let resultExpectation = expectation(description: "result block must be called.")
|
||||
plugin.handle(call) { result in
|
||||
XCTAssertNil(result, "result block must be called with nil.")
|
||||
resultExpectation.fulfill()
|
||||
}
|
||||
plugin.clearShortcutItems()
|
||||
|
||||
XCTAssertEqual(mockShortcutItemProvider.shortcutItems, [], "Must clear shortcut items.")
|
||||
waitForExpectations(timeout: 1)
|
||||
}
|
||||
|
||||
func testHandleMethodCall_getLaunchAction() {
|
||||
let call = FlutterMethodCall(methodName: "getLaunchAction", arguments: nil)
|
||||
|
||||
let mockChannel = MockMethodChannel()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
|
||||
let resultExpectation = expectation(description: "result block must be called.")
|
||||
plugin.handle(call) { result in
|
||||
XCTAssertNil(result, "result block must be called with nil.")
|
||||
resultExpectation.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 1)
|
||||
}
|
||||
|
||||
func testHandleMethodCall_nonExistMethods() {
|
||||
let call = FlutterMethodCall(methodName: "nonExist", arguments: nil)
|
||||
|
||||
let mockChannel = MockMethodChannel()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
|
||||
let resultExpectation = expectation(description: "result block must be called.")
|
||||
|
||||
plugin.handle(call) { result in
|
||||
XCTAssertEqual(
|
||||
result as? NSObject, FlutterMethodNotImplemented,
|
||||
"result block must be called with FlutterMethodNotImplemented")
|
||||
resultExpectation.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 1)
|
||||
}
|
||||
|
||||
func testApplicationPerformActionForShortcutItem() {
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
let item = UIApplicationShortcutItem(
|
||||
type: "SearchTheThing",
|
||||
@ -143,30 +85,29 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
userInfo: nil)
|
||||
|
||||
let invokeMethodExpectation = expectation(description: "invokeMethod must be called.")
|
||||
mockChannel.invokeMethodStub = { method, arguments in
|
||||
XCTAssertEqual(method, "launch")
|
||||
XCTAssertEqual(arguments as? String, item.type)
|
||||
flutterApi.launchActionCallback = { aString in
|
||||
XCTAssertEqual(aString, item.type)
|
||||
invokeMethodExpectation.fulfill()
|
||||
}
|
||||
|
||||
let actionResult = plugin.application(
|
||||
UIApplication.shared,
|
||||
performActionFor: item
|
||||
) { success in /* no-op */ }
|
||||
) { success in
|
||||
// noop
|
||||
}
|
||||
|
||||
XCTAssert(actionResult, "performActionForShortcutItem must return true.")
|
||||
waitForExpectations(timeout: 1)
|
||||
}
|
||||
|
||||
func testApplicationDidFinishLaunchingWithOptions_launchWithShortcut() {
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
let item = UIApplicationShortcutItem(
|
||||
type: "SearchTheThing",
|
||||
@ -183,14 +124,12 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut() {
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
let launchResult = plugin.application(UIApplication.shared, didFinishLaunchingWithOptions: [:])
|
||||
XCTAssert(
|
||||
@ -198,18 +137,12 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testApplicationDidBecomeActive_launchWithoutShortcut() {
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
|
||||
mockChannel.invokeMethodStub = { _, _ in
|
||||
XCTFail("invokeMethod should not be called if launch without shortcut.")
|
||||
}
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
let launchResult = plugin.application(UIApplication.shared, didFinishLaunchingWithOptions: [:])
|
||||
XCTAssert(
|
||||
@ -226,19 +159,16 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
icon: UIApplicationShortcutIcon(templateImageName: "search_the_thing.png"),
|
||||
userInfo: nil)
|
||||
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
let invokeMethodExpectation = expectation(description: "invokeMethod must be called.")
|
||||
mockChannel.invokeMethodStub = { method, arguments in
|
||||
XCTAssertEqual(method, "launch")
|
||||
XCTAssertEqual(arguments as? String, item.type)
|
||||
flutterApi.launchActionCallback = { aString in
|
||||
XCTAssertEqual(aString, item.type)
|
||||
invokeMethodExpectation.fulfill()
|
||||
}
|
||||
|
||||
@ -261,20 +191,19 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
icon: UIApplicationShortcutIcon(templateImageName: "search_the_thing.png"),
|
||||
userInfo: nil)
|
||||
|
||||
let mockChannel = MockMethodChannel()
|
||||
let flutterApi: MockFlutterApi = MockFlutterApi()
|
||||
let mockShortcutItemProvider = MockShortcutItemProvider()
|
||||
let mockShortcutItemParser = MockShortcutItemParser()
|
||||
|
||||
let plugin = QuickActionsPlugin(
|
||||
channel: mockChannel,
|
||||
shortcutItemProvider: mockShortcutItemProvider,
|
||||
shortcutItemParser: mockShortcutItemParser)
|
||||
flutterApi: flutterApi,
|
||||
shortcutItemProvider: mockShortcutItemProvider)
|
||||
|
||||
let invokeMethodExpectation = expectation(description: "invokeMethod must be called.")
|
||||
|
||||
var invokeMehtodCount = 0
|
||||
mockChannel.invokeMethodStub = { method, arguments in
|
||||
invokeMehtodCount += 1
|
||||
var invokeMethodCount = 0
|
||||
flutterApi.launchActionCallback = { aString in
|
||||
XCTAssertEqual(aString, item.type)
|
||||
invokeMethodCount += 1
|
||||
invokeMethodExpectation.fulfill()
|
||||
}
|
||||
|
||||
@ -288,7 +217,6 @@ class QuickActionsPluginTests: XCTestCase {
|
||||
plugin.applicationDidBecomeActive(UIApplication.shared)
|
||||
waitForExpectations(timeout: 1)
|
||||
|
||||
XCTAssertEqual(invokeMehtodCount, 1, "shortcut should only be handled once per launch.")
|
||||
XCTAssertEqual(invokeMethodCount, 1, "shortcut should only be handled once per launch.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
// 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 Flutter
|
||||
|
||||
/// A channel for platform code to communicate with the Dart code.
|
||||
protocol MethodChannel {
|
||||
/// Invokes a method in Dart code.
|
||||
/// - Parameter method the method name.
|
||||
/// - Parameter arguments the method arguments.
|
||||
func invokeMethod(_ method: String, arguments: Any?)
|
||||
}
|
||||
|
||||
/// A default implementation of the `MethodChannel` protocol.
|
||||
extension FlutterMethodChannel: MethodChannel {}
|
@ -4,48 +4,36 @@
|
||||
|
||||
import Flutter
|
||||
|
||||
public final class QuickActionsPlugin: NSObject, FlutterPlugin {
|
||||
public final class QuickActionsPlugin: NSObject, FlutterPlugin, IOSQuickActionsApi {
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(
|
||||
name: "plugins.flutter.io/quick_actions_ios",
|
||||
binaryMessenger: registrar.messenger())
|
||||
let instance = QuickActionsPlugin(channel: channel)
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
let messenger = registrar.messenger()
|
||||
let flutterApi = IOSQuickActionsFlutterApi(binaryMessenger: messenger)
|
||||
let instance = QuickActionsPlugin(flutterApi: flutterApi)
|
||||
IOSQuickActionsApiSetup.setUp(binaryMessenger: messenger, api: instance)
|
||||
registrar.addApplicationDelegate(instance)
|
||||
}
|
||||
|
||||
private let channel: MethodChannel
|
||||
private let shortcutItemProvider: ShortcutItemProviding
|
||||
private let shortcutItemParser: ShortcutItemParser
|
||||
private let flutterApi: IOSQuickActionsFlutterApiProtocol
|
||||
/// The type of the shortcut item selected when launching the app.
|
||||
private var launchingShortcutType: String? = nil
|
||||
|
||||
init(
|
||||
channel: MethodChannel,
|
||||
shortcutItemProvider: ShortcutItemProviding = UIApplication.shared,
|
||||
shortcutItemParser: ShortcutItemParser = DefaultShortcutItemParser()
|
||||
flutterApi: IOSQuickActionsFlutterApiProtocol,
|
||||
shortcutItemProvider: ShortcutItemProviding = UIApplication.shared
|
||||
) {
|
||||
self.channel = channel
|
||||
self.flutterApi = flutterApi
|
||||
self.shortcutItemProvider = shortcutItemProvider
|
||||
self.shortcutItemParser = shortcutItemParser
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "setShortcutItems":
|
||||
// `arguments` must be an array of dictionaries
|
||||
let items = call.arguments as! [[String: Any]]
|
||||
shortcutItemProvider.shortcutItems = shortcutItemParser.parseShortcutItems(items)
|
||||
result(nil)
|
||||
case "clearShortcutItems":
|
||||
shortcutItemProvider.shortcutItems = []
|
||||
result(nil)
|
||||
case "getLaunchAction":
|
||||
result(nil)
|
||||
case _:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
func setShortcutItems(itemsList: [ShortcutItemMessage]) {
|
||||
shortcutItemProvider.shortcutItems =
|
||||
convertShortcutItemMessageListToUIApplicationShortcutItemList(itemsList)
|
||||
}
|
||||
|
||||
func clearShortcutItems() {
|
||||
shortcutItemProvider.shortcutItems = []
|
||||
}
|
||||
|
||||
public func application(
|
||||
@ -84,8 +72,37 @@ public final class QuickActionsPlugin: NSObject, FlutterPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private func handleShortcut(_ shortcut: String) {
|
||||
channel.invokeMethod("launch", arguments: shortcut)
|
||||
func handleShortcut(_ shortcut: String) {
|
||||
flutterApi.launchAction(action: shortcut) { _ in
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
private func convertShortcutItemMessageListToUIApplicationShortcutItemList(
|
||||
_ items: [ShortcutItemMessage]
|
||||
) -> [UIApplicationShortcutItem] {
|
||||
return items.compactMap { convertShortcutItemMessageToUIApplicationShortcutItem(with: $0) }
|
||||
}
|
||||
|
||||
private func convertShortcutItemMessageToUIApplicationShortcutItem(
|
||||
with shortcut: ShortcutItemMessage
|
||||
)
|
||||
-> UIApplicationShortcutItem?
|
||||
{
|
||||
|
||||
let type = shortcut.type
|
||||
let localizedTitle = shortcut.localizedTitle
|
||||
|
||||
let icon = (shortcut.icon).map {
|
||||
UIApplicationShortcutIcon(templateImageName: $0)
|
||||
}
|
||||
|
||||
// type and localizedTitle are required.
|
||||
return UIApplicationShortcutItem(
|
||||
type: type,
|
||||
localizedTitle: localizedTitle,
|
||||
localizedSubtitle: nil,
|
||||
icon: icon,
|
||||
userInfo: nil)
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
// 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 UIKit
|
||||
|
||||
/// A parser that parses an array of raw shortcut items.
|
||||
protocol ShortcutItemParser {
|
||||
|
||||
/// Parses an array of raw shortcut items into an array of UIApplicationShortcutItems
|
||||
///
|
||||
/// - Parameter items an array of raw shortcut items to be parsed.
|
||||
/// - Returns an array of parsed shortcut items to be set.
|
||||
///
|
||||
func parseShortcutItems(_ items: [[String: Any]]) -> [UIApplicationShortcutItem]
|
||||
}
|
||||
|
||||
/// A default implementation of the `ShortcutItemParser` protocol.
|
||||
final class DefaultShortcutItemParser: ShortcutItemParser {
|
||||
|
||||
func parseShortcutItems(_ items: [[String: Any]]) -> [UIApplicationShortcutItem] {
|
||||
return items.compactMap { deserializeShortcutItem(with: $0) }
|
||||
}
|
||||
|
||||
private func deserializeShortcutItem(with serialized: [String: Any]) -> UIApplicationShortcutItem?
|
||||
{
|
||||
guard
|
||||
let type = serialized["type"] as? String,
|
||||
let localizedTitle = serialized["localizedTitle"] as? String
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let icon = (serialized["icon"] as? String).map {
|
||||
UIApplicationShortcutIcon(templateImageName: $0)
|
||||
}
|
||||
|
||||
// type and localizedTitle are required.
|
||||
return UIApplicationShortcutItem(
|
||||
type: type,
|
||||
localizedTitle: localizedTitle,
|
||||
localizedSubtitle: nil,
|
||||
icon: icon,
|
||||
userInfo: nil)
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
// 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.
|
||||
// Autogenerated from Pigeon (v12.0.1), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
|
||||
import Foundation
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
extension FlutterError: Error {}
|
||||
|
||||
private func isNullish(_ value: Any?) -> Bool {
|
||||
return value is NSNull || value == nil
|
||||
}
|
||||
|
||||
private func wrapResult(_ result: Any?) -> [Any?] {
|
||||
return [result]
|
||||
}
|
||||
|
||||
private func wrapError(_ error: Any) -> [Any?] {
|
||||
if let flutterError = error as? FlutterError {
|
||||
return [
|
||||
flutterError.code,
|
||||
flutterError.message,
|
||||
flutterError.details
|
||||
]
|
||||
}
|
||||
return [
|
||||
"\(error)",
|
||||
"\(type(of: error))",
|
||||
"Stacktrace: \(Thread.callStackSymbols)"
|
||||
]
|
||||
}
|
||||
|
||||
private func nilOrValue<T>(_ value: Any?) -> T? {
|
||||
if value is NSNull { return nil }
|
||||
return value as! T?
|
||||
}
|
||||
|
||||
/// Home screen quick-action shortcut item.
|
||||
///
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct ShortcutItemMessage {
|
||||
/// The identifier of this item; should be unique within the app.
|
||||
var type: String
|
||||
/// Localized title of the item.
|
||||
var localizedTitle: String
|
||||
/// Name of native resource to be displayed as the icon for this item.
|
||||
var icon: String? = nil
|
||||
|
||||
static func fromList(_ list: [Any?]) -> ShortcutItemMessage? {
|
||||
let type = list[0] as! String
|
||||
let localizedTitle = list[1] as! String
|
||||
let icon: String? = nilOrValue(list[2])
|
||||
|
||||
return ShortcutItemMessage(
|
||||
type: type,
|
||||
localizedTitle: localizedTitle,
|
||||
icon: icon
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
type,
|
||||
localizedTitle,
|
||||
icon,
|
||||
]
|
||||
}
|
||||
}
|
||||
private class IOSQuickActionsApiCodecReader: FlutterStandardReader {
|
||||
override func readValue(ofType type: UInt8) -> Any? {
|
||||
switch type {
|
||||
case 128:
|
||||
return ShortcutItemMessage.fromList(self.readValue() as! [Any?])
|
||||
default:
|
||||
return super.readValue(ofType: type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IOSQuickActionsApiCodecWriter: FlutterStandardWriter {
|
||||
override func writeValue(_ value: Any) {
|
||||
if let value = value as? ShortcutItemMessage {
|
||||
super.writeByte(128)
|
||||
super.writeValue(value.toList())
|
||||
} else {
|
||||
super.writeValue(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IOSQuickActionsApiCodecReaderWriter: FlutterStandardReaderWriter {
|
||||
override func reader(with data: Data) -> FlutterStandardReader {
|
||||
return IOSQuickActionsApiCodecReader(data: data)
|
||||
}
|
||||
|
||||
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
|
||||
return IOSQuickActionsApiCodecWriter(data: data)
|
||||
}
|
||||
}
|
||||
|
||||
class IOSQuickActionsApiCodec: FlutterStandardMessageCodec {
|
||||
static let shared = IOSQuickActionsApiCodec(readerWriter: IOSQuickActionsApiCodecReaderWriter())
|
||||
}
|
||||
|
||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||
protocol IOSQuickActionsApi {
|
||||
/// Sets the dynamic shortcuts for the app.
|
||||
func setShortcutItems(itemsList: [ShortcutItemMessage]) throws
|
||||
/// Removes all dynamic shortcuts.
|
||||
func clearShortcutItems() throws
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
class IOSQuickActionsApiSetup {
|
||||
/// The codec used by IOSQuickActionsApi.
|
||||
static var codec: FlutterStandardMessageCodec { IOSQuickActionsApiCodec.shared }
|
||||
/// Sets up an instance of `IOSQuickActionsApi` to handle messages through the `binaryMessenger`.
|
||||
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: IOSQuickActionsApi?) {
|
||||
/// Sets the dynamic shortcuts for the app.
|
||||
let setShortcutItemsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsApi.setShortcutItems", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
setShortcutItemsChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let itemsListArg = args[0] as! [ShortcutItemMessage]
|
||||
do {
|
||||
try api.setShortcutItems(itemsList: itemsListArg)
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setShortcutItemsChannel.setMessageHandler(nil)
|
||||
}
|
||||
/// Removes all dynamic shortcuts.
|
||||
let clearShortcutItemsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsApi.clearShortcutItems", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
clearShortcutItemsChannel.setMessageHandler { _, reply in
|
||||
do {
|
||||
try api.clearShortcutItems()
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearShortcutItemsChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
|
||||
protocol IOSQuickActionsFlutterApiProtocol {
|
||||
/// Sends a string representing a shortcut from the native platform to the app.
|
||||
func launchAction(action actionArg: String, completion: @escaping (Result<Void, FlutterError>) -> Void)
|
||||
}
|
||||
class IOSQuickActionsFlutterApi: IOSQuickActionsFlutterApiProtocol {
|
||||
private let binaryMessenger: FlutterBinaryMessenger
|
||||
init(binaryMessenger: FlutterBinaryMessenger){
|
||||
self.binaryMessenger = binaryMessenger
|
||||
}
|
||||
/// Sends a string representing a shortcut from the native platform to the app.
|
||||
func launchAction(action actionArg: String, completion: @escaping (Result<Void, FlutterError>) -> Void) {
|
||||
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsFlutterApi.launchAction", binaryMessenger: binaryMessenger)
|
||||
channel.sendMessage([actionArg] as [Any?]) { _ in
|
||||
completion(.success(Void()))
|
||||
}
|
||||
}
|
||||
}
|
178
packages/quick_actions/quick_actions_ios/lib/messages.g.dart
Normal file
178
packages/quick_actions/quick_actions_ios/lib/messages.g.dart
Normal file
@ -0,0 +1,178 @@
|
||||
// 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.
|
||||
// Autogenerated from Pigeon (v12.0.1), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||
|
||||
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
List<Object?> wrapResponse(
|
||||
{Object? result, PlatformException? error, bool empty = false}) {
|
||||
if (empty) {
|
||||
return <Object?>[];
|
||||
}
|
||||
if (error == null) {
|
||||
return <Object?>[result];
|
||||
}
|
||||
return <Object?>[error.code, error.message, error.details];
|
||||
}
|
||||
|
||||
/// Home screen quick-action shortcut item.
|
||||
class ShortcutItemMessage {
|
||||
ShortcutItemMessage({
|
||||
required this.type,
|
||||
required this.localizedTitle,
|
||||
this.icon,
|
||||
});
|
||||
|
||||
/// The identifier of this item; should be unique within the app.
|
||||
String type;
|
||||
|
||||
/// Localized title of the item.
|
||||
String localizedTitle;
|
||||
|
||||
/// Name of native resource to be displayed as the icon for this item.
|
||||
String? icon;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
type,
|
||||
localizedTitle,
|
||||
icon,
|
||||
];
|
||||
}
|
||||
|
||||
static ShortcutItemMessage decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return ShortcutItemMessage(
|
||||
type: result[0]! as String,
|
||||
localizedTitle: result[1]! as String,
|
||||
icon: result[2] as String?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _IOSQuickActionsApiCodec extends StandardMessageCodec {
|
||||
const _IOSQuickActionsApiCodec();
|
||||
@override
|
||||
void writeValue(WriteBuffer buffer, Object? value) {
|
||||
if (value is ShortcutItemMessage) {
|
||||
buffer.putUint8(128);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||
switch (type) {
|
||||
case 128:
|
||||
return ShortcutItemMessage.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IOSQuickActionsApi {
|
||||
/// Constructor for [IOSQuickActionsApi]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
IOSQuickActionsApi({BinaryMessenger? binaryMessenger})
|
||||
: _binaryMessenger = binaryMessenger;
|
||||
final BinaryMessenger? _binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> codec = _IOSQuickActionsApiCodec();
|
||||
|
||||
/// Sets the dynamic shortcuts for the app.
|
||||
Future<void> setShortcutItems(
|
||||
List<ShortcutItemMessage?> arg_itemsList) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsApi.setShortcutItems',
|
||||
codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_itemsList]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all dynamic shortcuts.
|
||||
Future<void> clearShortcutItems() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsApi.clearShortcutItems',
|
||||
codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IOSQuickActionsFlutterApi {
|
||||
static const MessageCodec<Object?> codec = StandardMessageCodec();
|
||||
|
||||
/// Sends a string representing a shortcut from the native platform to the app.
|
||||
void launchAction(String action);
|
||||
|
||||
static void setup(IOSQuickActionsFlutterApi? api,
|
||||
{BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsFlutterApi.launchAction',
|
||||
codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
channel.setMessageHandler(null);
|
||||
} else {
|
||||
channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsFlutterApi.launchAction was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final String? arg_action = (args[0] as String?);
|
||||
assert(arg_action != null,
|
||||
'Argument for dev.flutter.pigeon.quick_actions_ios.IOSQuickActionsFlutterApi.launchAction was null, expected non-null String.');
|
||||
try {
|
||||
api.launchAction(arg_action!);
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
} catch (e) {
|
||||
return wrapResponse(
|
||||
error: PlatformException(code: 'error', message: e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,54 +3,58 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart';
|
||||
|
||||
import 'messages.g.dart';
|
||||
|
||||
export 'package:quick_actions_platform_interface/types/types.dart';
|
||||
|
||||
const MethodChannel _channel =
|
||||
MethodChannel('plugins.flutter.io/quick_actions_ios');
|
||||
late QuickActionHandler _handler;
|
||||
|
||||
/// An implementation of [QuickActionsPlatform] for iOS.
|
||||
class QuickActionsIos extends QuickActionsPlatform {
|
||||
/// Creates a new plugin implementation instance.
|
||||
QuickActionsIos({
|
||||
@visibleForTesting IOSQuickActionsApi? api,
|
||||
}) : _hostApi = api ?? IOSQuickActionsApi();
|
||||
|
||||
final IOSQuickActionsApi _hostApi;
|
||||
|
||||
/// Registers this class as the default instance of [QuickActionsPlatform].
|
||||
static void registerWith() {
|
||||
QuickActionsPlatform.instance = QuickActionsIos();
|
||||
}
|
||||
|
||||
/// The MethodChannel that is being used by this implementation of the plugin.
|
||||
@visibleForTesting
|
||||
MethodChannel get channel => _channel;
|
||||
|
||||
@override
|
||||
Future<void> initialize(QuickActionHandler handler) async {
|
||||
channel.setMethodCallHandler((MethodCall call) async {
|
||||
assert(call.method == 'launch');
|
||||
handler(call.arguments as String);
|
||||
});
|
||||
final String? action =
|
||||
await channel.invokeMethod<String?>('getLaunchAction');
|
||||
if (action != null) {
|
||||
handler(action);
|
||||
}
|
||||
final _QuickActionHandlerApi quickActionsHandlerApi =
|
||||
_QuickActionHandlerApi();
|
||||
IOSQuickActionsFlutterApi.setup(quickActionsHandlerApi);
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setShortcutItems(List<ShortcutItem> items) async {
|
||||
final List<Map<String, String?>> itemsList =
|
||||
items.map(_serializeItem).toList();
|
||||
await channel.invokeMethod<void>('setShortcutItems', itemsList);
|
||||
await _hostApi.setShortcutItems(
|
||||
items.map(_shortcutItemToShortcutItemMessage).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearShortcutItems() =>
|
||||
channel.invokeMethod<void>('clearShortcutItems');
|
||||
Future<void> clearShortcutItems() => _hostApi.clearShortcutItems();
|
||||
|
||||
Map<String, String?> _serializeItem(ShortcutItem item) {
|
||||
return <String, String?>{
|
||||
'type': item.type,
|
||||
'localizedTitle': item.localizedTitle,
|
||||
'icon': item.icon,
|
||||
};
|
||||
ShortcutItemMessage _shortcutItemToShortcutItemMessage(ShortcutItem item) {
|
||||
return ShortcutItemMessage(
|
||||
type: item.type,
|
||||
localizedTitle: item.localizedTitle,
|
||||
icon: item.icon,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _QuickActionHandlerApi extends IOSQuickActionsFlutterApi {
|
||||
@override
|
||||
void launchAction(String action) {
|
||||
_handler(action);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
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.
|
@ -0,0 +1,44 @@
|
||||
// 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 'package:pigeon/pigeon.dart';
|
||||
|
||||
@ConfigurePigeon(PigeonOptions(
|
||||
dartOut: 'lib/messages.g.dart',
|
||||
swiftOut: 'ios/Classes/messages.g.swift',
|
||||
copyrightHeader: 'pigeons/copyright.txt',
|
||||
))
|
||||
|
||||
/// Home screen quick-action shortcut item.
|
||||
class ShortcutItemMessage {
|
||||
ShortcutItemMessage(
|
||||
this.type,
|
||||
this.localizedTitle,
|
||||
this.icon,
|
||||
);
|
||||
|
||||
/// The identifier of this item; should be unique within the app.
|
||||
String type;
|
||||
|
||||
/// Localized title of the item.
|
||||
String localizedTitle;
|
||||
|
||||
/// Name of native resource to be displayed as the icon for this item.
|
||||
String? icon;
|
||||
}
|
||||
|
||||
@HostApi()
|
||||
abstract class IOSQuickActionsApi {
|
||||
/// Sets the dynamic shortcuts for the app.
|
||||
void setShortcutItems(List<ShortcutItemMessage> itemsList);
|
||||
|
||||
/// Removes all dynamic shortcuts.
|
||||
void clearShortcutItems();
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class IOSQuickActionsFlutterApi {
|
||||
/// Sends a string representing a shortcut from the native platform to the app.
|
||||
void launchAction(String action);
|
||||
}
|
@ -2,7 +2,7 @@ name: quick_actions_ios
|
||||
description: An implementation for the iOS platform of the Flutter `quick_actions` plugin.
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/quick_actions/quick_actions_ios
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
|
||||
version: 1.0.7
|
||||
version: 1.0.8
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.0 <4.0.0"
|
||||
@ -26,6 +26,7 @@ dev_dependencies:
|
||||
sdk: flutter
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
pigeon: ^12.0.1
|
||||
plugin_platform_interface: ^2.1.2
|
||||
|
||||
topics:
|
||||
|
@ -2,171 +2,89 @@
|
||||
// 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';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:quick_actions_ios/messages.g.dart';
|
||||
import 'package:quick_actions_ios/quick_actions_ios.dart';
|
||||
import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart';
|
||||
|
||||
const String LAUNCH_ACTION_STRING = 'aString';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('$QuickActionsIos', () {
|
||||
late List<MethodCall> log;
|
||||
final _FakeQuickActionsApi api = _FakeQuickActionsApi();
|
||||
final QuickActionsIos quickActions = QuickActionsIos(api: api);
|
||||
|
||||
setUp(() {
|
||||
log = <MethodCall>[];
|
||||
});
|
||||
test('registerWith() registers correct instance', () {
|
||||
QuickActionsIos.registerWith();
|
||||
expect(QuickActionsPlatform.instance, isA<QuickActionsIos>());
|
||||
});
|
||||
|
||||
QuickActionsIos buildQuickActionsPlugin() {
|
||||
final QuickActionsIos quickActions = QuickActionsIos();
|
||||
_ambiguate(TestDefaultBinaryMessengerBinding.instance)!
|
||||
.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(quickActions.channel,
|
||||
(MethodCall methodCall) async {
|
||||
log.add(methodCall);
|
||||
return '';
|
||||
});
|
||||
|
||||
return quickActions;
|
||||
}
|
||||
|
||||
test('registerWith() registers correct instance', () {
|
||||
QuickActionsIos.registerWith();
|
||||
expect(QuickActionsPlatform.instance, isA<QuickActionsIos>());
|
||||
});
|
||||
|
||||
group('#initialize', () {
|
||||
test('passes getLaunchAction on launch method', () {
|
||||
final QuickActionsIos quickActions = buildQuickActionsPlugin();
|
||||
quickActions.initialize((String type) {});
|
||||
|
||||
expect(
|
||||
log,
|
||||
<Matcher>[
|
||||
isMethodCall('getLaunchAction', arguments: null),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('initialize', () async {
|
||||
final QuickActionsIos quickActions = buildQuickActionsPlugin();
|
||||
final Completer<bool> quickActionsHandler = Completer<bool>();
|
||||
await quickActions
|
||||
.initialize((_) => quickActionsHandler.complete(true));
|
||||
expect(
|
||||
log,
|
||||
<Matcher>[
|
||||
isMethodCall('getLaunchAction', arguments: null),
|
||||
],
|
||||
);
|
||||
log.clear();
|
||||
|
||||
expect(quickActionsHandler.future, completion(isTrue));
|
||||
});
|
||||
});
|
||||
|
||||
group('#setShortCutItems', () {
|
||||
test('passes shortcutItem through channel', () {
|
||||
final QuickActionsIos quickActions = buildQuickActionsPlugin();
|
||||
quickActions.initialize((String type) {});
|
||||
quickActions.setShortcutItems(<ShortcutItem>[
|
||||
const ShortcutItem(
|
||||
type: 'test', localizedTitle: 'title', icon: 'icon.svg')
|
||||
]);
|
||||
|
||||
expect(
|
||||
log,
|
||||
<Matcher>[
|
||||
isMethodCall('getLaunchAction', arguments: null),
|
||||
isMethodCall('setShortcutItems', arguments: <Map<String, String>>[
|
||||
<String, String>{
|
||||
'type': 'test',
|
||||
'localizedTitle': 'title',
|
||||
'icon': 'icon.svg',
|
||||
}
|
||||
]),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('setShortcutItems with demo data', () async {
|
||||
const String type = 'type';
|
||||
const String localizedTitle = 'localizedTitle';
|
||||
const String icon = 'icon';
|
||||
final QuickActionsIos quickActions = buildQuickActionsPlugin();
|
||||
await quickActions.setShortcutItems(
|
||||
const <ShortcutItem>[
|
||||
ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon)
|
||||
],
|
||||
);
|
||||
expect(
|
||||
log,
|
||||
<Matcher>[
|
||||
isMethodCall(
|
||||
'setShortcutItems',
|
||||
arguments: <Map<String, String>>[
|
||||
<String, String>{
|
||||
'type': type,
|
||||
'localizedTitle': localizedTitle,
|
||||
'icon': icon,
|
||||
}
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
log.clear();
|
||||
});
|
||||
});
|
||||
|
||||
group('#clearShortCutItems', () {
|
||||
test('send clearShortcutItems through channel', () {
|
||||
final QuickActionsIos quickActions = buildQuickActionsPlugin();
|
||||
quickActions.initialize((String type) {});
|
||||
quickActions.clearShortcutItems();
|
||||
|
||||
expect(
|
||||
log,
|
||||
<Matcher>[
|
||||
isMethodCall('getLaunchAction', arguments: null),
|
||||
isMethodCall('clearShortcutItems', arguments: null),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('clearShortcutItems', () {
|
||||
final QuickActionsIos quickActions = buildQuickActionsPlugin();
|
||||
quickActions.clearShortcutItems();
|
||||
expect(
|
||||
log,
|
||||
<Matcher>[
|
||||
isMethodCall('clearShortcutItems', arguments: null),
|
||||
],
|
||||
);
|
||||
log.clear();
|
||||
});
|
||||
group('#initialize', () {
|
||||
test('initialize', () {
|
||||
expect(quickActions.initialize((_) {}), completes);
|
||||
});
|
||||
});
|
||||
|
||||
group('$ShortcutItem', () {
|
||||
test('Shortcut item can be constructed', () {
|
||||
const String type = 'type';
|
||||
const String localizedTitle = 'title';
|
||||
const String icon = 'foo';
|
||||
test('setShortcutItems', () async {
|
||||
await quickActions.initialize((String type) {});
|
||||
const ShortcutItem item =
|
||||
ShortcutItem(type: 'test', localizedTitle: 'title', icon: 'icon.svg');
|
||||
await quickActions.setShortcutItems(<ShortcutItem>[item]);
|
||||
|
||||
const ShortcutItem item =
|
||||
ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon);
|
||||
expect(api.items.first.type, item.type);
|
||||
expect(api.items.first.localizedTitle, item.localizedTitle);
|
||||
expect(api.items.first.icon, item.icon);
|
||||
});
|
||||
|
||||
expect(item.type, type);
|
||||
expect(item.localizedTitle, localizedTitle);
|
||||
expect(item.icon, icon);
|
||||
});
|
||||
test('clearShortCutItems', () {
|
||||
quickActions.initialize((String type) {});
|
||||
const ShortcutItem item =
|
||||
ShortcutItem(type: 'test', localizedTitle: 'title', icon: 'icon.svg');
|
||||
quickActions.setShortcutItems(<ShortcutItem>[item]);
|
||||
quickActions.clearShortcutItems();
|
||||
|
||||
expect(api.items.isEmpty, true);
|
||||
});
|
||||
|
||||
test('Shortcut item can be constructed', () {
|
||||
const String type = 'type';
|
||||
const String localizedTitle = 'title';
|
||||
const String icon = 'foo';
|
||||
|
||||
const ShortcutItem item =
|
||||
ShortcutItem(type: type, localizedTitle: localizedTitle, icon: icon);
|
||||
|
||||
expect(item.type, type);
|
||||
expect(item.localizedTitle, localizedTitle);
|
||||
expect(item.icon, icon);
|
||||
});
|
||||
}
|
||||
|
||||
/// This allows a value of type T or T? to be treated as a value of type T?.
|
||||
///
|
||||
/// We use this so that APIs that have become non-nullable can still be used
|
||||
/// with `!` and `?` on the stable branch.
|
||||
T? _ambiguate<T>(T? value) => value;
|
||||
class _FakeQuickActionsApi implements IOSQuickActionsApi {
|
||||
List<ShortcutItem> items = <ShortcutItem>[];
|
||||
bool getLaunchActionCalled = false;
|
||||
|
||||
@override
|
||||
Future<void> clearShortcutItems() async {
|
||||
items = <ShortcutItem>[];
|
||||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setShortcutItems(List<ShortcutItemMessage?> itemsList) async {
|
||||
await clearShortcutItems();
|
||||
for (final ShortcutItemMessage? element in itemsList) {
|
||||
items.add(shortcutItemMessageToShortcutItem(element!));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion tool to change [ShortcutItemMessage] back to [ShortcutItem]
|
||||
ShortcutItem shortcutItemMessageToShortcutItem(ShortcutItemMessage item) {
|
||||
return ShortcutItem(
|
||||
type: item.type,
|
||||
localizedTitle: item.localizedTitle,
|
||||
icon: item.icon,
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user