Commit 8a108a53bff922d0ac4002b278a554ff06aaa124

Authored by 权海
1 parent 9956ee47

feat(ui):手表自定义主题支持

... ... @@ -134,6 +134,7 @@ private open class WearEngineApiPigeonCodec : StandardMessageCodec() {
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface WearEngineHostApi {
fun hasAvailableDevices(): Boolean
... ... @@ -145,7 +146,7 @@ interface WearEngineHostApi {
* Opens the system photo picker and returns a local PNG file path after
* removing the image background on the host platform.
*/
fun pickImageAndRemoveBackground(): String?
fun removeBackground(originImagePath: String, callback: (Result<String?>) -> Unit)
companion object {
/** The codec used by WearEngineHostApi. */
... ... @@ -236,15 +237,20 @@ interface WearEngineHostApi {
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.WearEngineHostApi.pickImageAndRemoveBackground$separatedMessageChannelSuffix", codec)
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.WearEngineHostApi.removeBackground$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
listOf(api.pickImageAndRemoveBackground())
} catch (exception: Throwable) {
WearEngineApiPigeonUtils.wrapError(exception)
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val originImagePathArg = args[0] as String
api.removeBackground(originImagePathArg) { result: Result<String?> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(WearEngineApiPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(WearEngineApiPigeonUtils.wrapResult(data))
}
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
... ...
... ... @@ -7,16 +7,16 @@
objects = {
/* Begin PBXBuildFile section */
0798E27DB95F2A823881D75A /* Pods_Runner_Watch_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A488EFC0BD642ED604C4E43 /* Pods_Runner_Watch_App.framework */; };
20A3C4994C9B0F60D1F3BCBE /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8DC48B3736B7FEB548D0E65 /* Pods_Runner.framework */; };
36345731C945926C4A5BD632 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEEADFA95177019D949C8D0 /* Pods_Runner.framework */; };
66FB19002FDBC80100F515B4 /* DFWatchFigmaTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FB19032FDBC80100F515B4 /* DFWatchFigmaTokens.swift */; };
66FB19012FDBC80100F515B4 /* DFWatchFigmaHomeViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FB19042FDBC80100F515B4 /* DFWatchFigmaHomeViews.swift */; };
66FB19022FDBC80100F515B4 /* DFWatchFigmaDetailViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FB19052FDBC80100F515B4 /* DFWatchFigmaDetailViews.swift */; };
66FBE58C2FDA4F0F00F515B4 /* Runner Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 66FBE58B2FDA4F0F00F515B4 /* Runner Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D0E7FB41C5122A32579199E1 /* Pods_Runner_Watch_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 32675A83AFB845DC8CA3467C /* Pods_Runner_Watch_App.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
66FBE58D2FDA4F0F00F515B4 /* PBXContainerItemProxy */ = {
66569B6E2FE27D0400B7E33C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 66FBE5762FDA4F0E00F515B4 /* Project object */;
proxyType = 1;
... ... @@ -50,10 +50,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
276A214460495EB498491851 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
27E4D5A8995AB1EC91E7330E /* Pods-Runner Watch App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner Watch App.debug.xcconfig"; path = "Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App.debug.xcconfig"; sourceTree = "<group>"; };
0B1A5890F53172BA3C93CF69 /* Pods-Runner Watch App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner Watch App.release.xcconfig"; path = "Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App.release.xcconfig"; sourceTree = "<group>"; };
32675A83AFB845DC8CA3467C /* Pods_Runner_Watch_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner_Watch_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4A9B7C102FDB0A1200F515B4 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
4A9B7C112FDB0A1200F515B4 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
5CEEADFA95177019D949C8D0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6291BE76A39C6E31BC18338B /* Pods-Runner Watch App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner Watch App.debug.xcconfig"; path = "Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App.debug.xcconfig"; sourceTree = "<group>"; };
66FB19032FDBC80100F515B4 /* DFWatchFigmaTokens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Runner Watch App/FigmaHome/DFWatchFigmaTokens.swift"; sourceTree = "<group>"; };
66FB19042FDBC80100F515B4 /* DFWatchFigmaHomeViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Runner Watch App/FigmaHome/DFWatchFigmaHomeViews.swift"; sourceTree = "<group>"; };
66FB19052FDBC80100F515B4 /* DFWatchFigmaDetailViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Runner Watch App/FigmaHome/DFWatchFigmaDetailViews.swift"; sourceTree = "<group>"; };
... ... @@ -62,10 +64,8 @@
66FBE5A72FDA518C00F515B4 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
66FBE5A92FDA518C00F515B4 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
66FBE5BF2FDA51A300F515B4 /* doublefeel-watch-app-extensionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "doublefeel-watch-app-extensionExtension.entitlements"; sourceTree = "<group>"; };
6F3AFF2C02DF9B5A3593F751 /* Pods-Runner Watch App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner Watch App.release.xcconfig"; path = "Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App.release.xcconfig"; sourceTree = "<group>"; };
7A488EFC0BD642ED604C4E43 /* Pods_Runner_Watch_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner_Watch_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B245A68D9E1E49A7A1C5C57A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
E8DC48B3736B7FEB548D0E65 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7845C91D0615F45450905891 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
9D4354F0FB273F6846CD38C3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
... ... @@ -109,7 +109,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
20A3C4994C9B0F60D1F3BCBE /* Pods_Runner.framework in Frameworks */,
36345731C945926C4A5BD632 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -117,7 +117,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0798E27DB95F2A823881D75A /* Pods_Runner_Watch_App.framework in Frameworks */,
D0E7FB41C5122A32579199E1 /* Pods_Runner_Watch_App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -171,8 +171,8 @@
children = (
66FBE5A72FDA518C00F515B4 /* WidgetKit.framework */,
66FBE5A92FDA518C00F515B4 /* SwiftUI.framework */,
E8DC48B3736B7FEB548D0E65 /* Pods_Runner.framework */,
7A488EFC0BD642ED604C4E43 /* Pods_Runner_Watch_App.framework */,
5CEEADFA95177019D949C8D0 /* Pods_Runner.framework */,
32675A83AFB845DC8CA3467C /* Pods_Runner_Watch_App.framework */,
);
name = Frameworks;
sourceTree = "<group>";
... ... @@ -180,10 +180,10 @@
C9C45D06AF94828256154164 /* Pods */ = {
isa = PBXGroup;
children = (
276A214460495EB498491851 /* Pods-Runner.debug.xcconfig */,
B245A68D9E1E49A7A1C5C57A /* Pods-Runner.release.xcconfig */,
27E4D5A8995AB1EC91E7330E /* Pods-Runner Watch App.debug.xcconfig */,
6F3AFF2C02DF9B5A3593F751 /* Pods-Runner Watch App.release.xcconfig */,
9D4354F0FB273F6846CD38C3 /* Pods-Runner.debug.xcconfig */,
7845C91D0615F45450905891 /* Pods-Runner.release.xcconfig */,
6291BE76A39C6E31BC18338B /* Pods-Runner Watch App.debug.xcconfig */,
0B1A5890F53172BA3C93CF69 /* Pods-Runner Watch App.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
... ... @@ -195,20 +195,20 @@
isa = PBXNativeTarget;
buildConfigurationList = 66FBE59C2FDA4F1000F515B4 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
650BBC2B60529BD5A359701D /* [CP] Check Pods Manifest.lock */,
90A53D3C4EC7070BDEF1286D /* [CP] Check Pods Manifest.lock */,
66FBE57A2FDA4F0E00F515B4 /* Sources */,
66FBE57B2FDA4F0E00F515B4 /* Frameworks */,
66FBE57C2FDA4F0E00F515B4 /* Resources */,
66FBE59B2FDA4F1000F515B4 /* Embed Watch Content */,
66FB07192FDAA64500F515B4 /* ShellScript */,
66FB071A2FDAA65C00F515B4 /* Thin Binary */,
AEED982AFD101BCEF5E9554C /* [CP] Embed Pods Frameworks */,
4F50D89F3617C0F6FEFA4C4F /* [CP] Copy Pods Resources */,
4348DC7B38CB739C4ABA7DAC /* [CP] Embed Pods Frameworks */,
E68ABE0C79927AE95370205A /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
66FBE58E2FDA4F0F00F515B4 /* PBXTargetDependency */,
66569B6F2FE27D0400B7E33C /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
66FBE5802FDA4F0E00F515B4 /* Runner */,
... ... @@ -222,12 +222,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 66FBE5982FDA4F1000F515B4 /* Build configuration list for PBXNativeTarget "Runner Watch App" */;
buildPhases = (
F7289009DDDC1ECAC786520D /* [CP] Check Pods Manifest.lock */,
BA66C644AE443BFC9EB5FA40 /* [CP] Check Pods Manifest.lock */,
66FBE5872FDA4F0F00F515B4 /* Sources */,
66FBE5882FDA4F0F00F515B4 /* Frameworks */,
66FBE5892FDA4F0F00F515B4 /* Resources */,
66FBE5BE2FDA518D00F515B4 /* Embed Foundation Extensions */,
DFE431650B789D7B2379EBFD /* [CP] Embed Pods Frameworks */,
1F69C09D185F42507815E3C2 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
... ... @@ -297,43 +297,38 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
4F50D89F3617C0F6FEFA4C4F /* [CP] Copy Pods Resources */ = {
1F69C09D185F42507815E3C2 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
650BBC2B60529BD5A359701D /* [CP] Check Pods Manifest.lock */ = {
4348DC7B38CB739C4ABA7DAC /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
66FB07192FDAA64500F515B4 /* ShellScript */ = {
... ... @@ -371,60 +366,65 @@
shellPath = /bin/sh;
shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
};
AEED982AFD101BCEF5E9554C /* [CP] Embed Pods Frameworks */ = {
90A53D3C4EC7070BDEF1286D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
DFE431650B789D7B2379EBFD /* [CP] Embed Pods Frameworks */ = {
BA66C644AE443BFC9EB5FA40 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner Watch App-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner Watch App/Pods-Runner Watch App-frameworks.sh\"\n";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
F7289009DDDC1ECAC786520D /* [CP] Check Pods Manifest.lock */ = {
E68ABE0C79927AE95370205A /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner Watch App-checkManifestLockResult.txt",
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
... ... @@ -450,10 +450,10 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
66FBE58E2FDA4F0F00F515B4 /* PBXTargetDependency */ = {
66569B6F2FE27D0400B7E33C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 66FBE58A2FDA4F0F00F515B4 /* Runner Watch App */;
targetProxy = 66FBE58D2FDA4F0F00F515B4 /* PBXContainerItemProxy */;
targetProxy = 66569B6E2FE27D0400B7E33C /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
... ... @@ -576,7 +576,7 @@
};
66FBE5992FDA4F1000F515B4 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 27E4D5A8995AB1EC91E7330E /* Pods-Runner Watch App.debug.xcconfig */;
baseConfigurationReference = 6291BE76A39C6E31BC18338B /* Pods-Runner Watch App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
... ... @@ -613,7 +613,7 @@
};
66FBE59A2FDA4F1000F515B4 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6F3AFF2C02DF9B5A3593F751 /* Pods-Runner Watch App.release.xcconfig */;
baseConfigurationReference = 0B1A5890F53172BA3C93CF69 /* Pods-Runner Watch App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
... ...
... ... @@ -181,6 +181,7 @@ class WearEngineApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable
static let shared = WearEngineApiPigeonCodec(readerWriter: WearEngineApiPigeonCodecReaderWriter())
}
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol WearEngineHostApi {
func hasAvailableDevices() throws -> Bool
... ... @@ -190,7 +191,7 @@ protocol WearEngineHostApi {
func sendWatchSyncPayload(jsonPayload: String) throws -> Bool
/// Opens the system photo picker and returns a local PNG file path after
/// removing the image background on the host platform.
func pickImageAndRemoveBackground() throws -> String?
func removeBackground(originImagePath: String, completion: @escaping (Result<String?, Error>) -> Void)
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
... ... @@ -270,18 +271,22 @@ class WearEngineHostApiSetup {
}
/// Opens the system photo picker and returns a local PNG file path after
/// removing the image background on the host platform.
let pickImageAndRemoveBackgroundChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.WearEngineHostApi.pickImageAndRemoveBackground\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
let removeBackgroundChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.WearEngineHostApi.removeBackground\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
pickImageAndRemoveBackgroundChannel.setMessageHandler { _, reply in
do {
let result = try api.pickImageAndRemoveBackground()
reply(wrapResult(result))
} catch {
reply(wrapError(error))
removeBackgroundChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let originImagePathArg = args[0] as! String
api.removeBackground(originImagePath: originImagePathArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
pickImageAndRemoveBackgroundChannel.setMessageHandler(nil)
removeBackgroundChannel.setMessageHandler(nil)
}
}
}
... ...
... ... @@ -3,6 +3,10 @@ import PhotosUI
import UIKit
import Vision
enum ImageProcessError: Error{
case invalidatePath
}
final class WearEngineHostApiImpl: WearEngineHostApi {
private let watchService: WatchConnectivityService
... ... @@ -30,100 +34,21 @@ final class WearEngineHostApiImpl: WearEngineHostApi {
watchService.syncPayload(jsonPayload: jsonPayload)
}
func pickImageAndRemoveBackground() throws -> String? {
if #available(iOS 14.0, *) {
return WatchThemeImagePicker().pickImageAndRemoveBackground()
}
return nil
}
}
@available(iOS 14.0, *)
private final class WatchThemeImagePicker: NSObject, PHPickerViewControllerDelegate {
private var continuation: CheckedContinuation<String?, Never>?
func pickImageAndRemoveBackground() -> String? {
if Thread.isMainThread {
return runOnMain()
}
var result: String?
let semaphore = DispatchSemaphore(value: 0)
DispatchQueue.main.async {
result = self.runOnMain()
semaphore.signal()
}
semaphore.wait()
return result
}
private func runOnMain() -> String? {
var configuration = PHPickerConfiguration(photoLibrary: .shared())
configuration.filter = .images
configuration.selectionLimit = 1
guard let presenter = UIApplication.shared.topMostViewController else {
return nil
}
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
return waitForPickerResult(picker: picker, presenter: presenter)
}
private func waitForPickerResult(
picker: PHPickerViewController,
presenter: UIViewController
) -> String? {
var pickedPath: String?
let semaphore = DispatchSemaphore(value: 0)
Task { @MainActor in
pickedPath = await withCheckedContinuation { continuation in
self.continuation = continuation
presenter.present(picker, animated: true)
}
semaphore.signal()
}
while semaphore.wait(timeout: .now() + 0.05) == .timedOut {
RunLoop.main.run(mode: .default, before: Date(timeIntervalSinceNow: 0.05))
}
return pickedPath
}
nonisolated func picker(
_ picker: PHPickerViewController,
didFinishPicking results: [PHPickerResult]
) {
Task { @MainActor in
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider,
provider.canLoadObject(ofClass: UIImage.self) else {
continuation?.resume(returning: nil)
continuation = nil
return
}
provider.loadObject(ofClass: UIImage.self) { object, _ in
Task { @MainActor in
guard let image = object as? UIImage else {
self.continuation?.resume(returning: nil)
self.continuation = nil
func removeBackground(originImagePath: String, completion: @escaping (Result<String?, any Error>) -> Void) {
// 抠图
guard let image = UIImage(contentsOfFile: originImagePath) else{
completion(.failure(ImageProcessError.invalidatePath))
return
}
let processed = await WatchThemeImageProcessor.removeBackground(from: image)
let path = WatchThemeImageProcessor.savePNG(processed)
self.continuation?.resume(returning: path)
self.continuation = nil
}
}
Task{
let processed = await WatchThemeImageProcessor.removeBackground(from: image)
let path = WatchThemeImageProcessor.savePNG(processed)
completion(.success(path))
}
}
}
}
private enum WatchThemeImageProcessor {
static func removeBackground(from image: UIImage) async -> UIImage {
guard #available(iOS 17.0, *),
... ...
... ... @@ -5,6 +5,7 @@ import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:doublefeel_flutter/pigeon/wear_engine_api.g.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import '../models/watch_theme_models.dart';
import '../widgets/watch_theme_dialogs.dart';
... ... @@ -13,6 +14,7 @@ class CreateWatchThemeController extends GetxController {
CreateWatchThemeController(this._themeApi);
final ThemeApi _themeApi;
final WearEngineHostApi _wearEngineHostApi = WearEngineHostApi();
final customThemeName = ''.obs;
final agreedToSubmission = true.obs;
... ... @@ -43,13 +45,11 @@ class CreateWatchThemeController extends GetxController {
customImagePaths.every((path) => path != null && path.isNotEmpty);
Future<void> pickCustomImage(int index) async {
try {
final path = await WearEngineHostApi().pickImageAndRemoveBackground();
if (path == null || path.isEmpty) {
return;
}
customImagePaths[index] = path;
} catch (error) {
final picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
if (image != null) {
customImagePaths[index] = image.path;
} else {
AppToast.show('图片选择失败');
}
}
... ... @@ -95,26 +95,58 @@ class CreateWatchThemeController extends GetxController {
}
isSaving.value = true;
final result = await _themeApi.createTheme(
name: customThemeName.value,
fullOfEnergyName: customStatusNames[0],
normalName: customStatusNames[1],
overpressureName: customStatusNames[3],
fullOfEnergyImage: customImagePaths[0] ?? '',
normalImage: customImagePaths[1] ?? '',
overpressureImage: customImagePaths[3] ?? customImagePaths[2] ?? '',
);
isSaving.value = false;
if (result is! AppSuccess<void>) {
AppToast.show('主题保存失败');
return;
var imagePath1 = customImagePaths[0];
var imagePath2 = customImagePaths[1];
var imagePath3 = customImagePaths[2];
var imagePath4 = customImagePaths[3];
if (imagePath1 != null) {
final path = await _wearEngineHostApi.removeBackground(imagePath1);
if (path != null) {
imagePath1 = path;
}
}
if (imagePath2 != null) {
final path = await _wearEngineHostApi.removeBackground(imagePath2);
if (path != null) {
imagePath2 = path;
}
}
if (imagePath3 != null) {
final path = await _wearEngineHostApi.removeBackground(imagePath3);
if (path != null) {
imagePath3 = path;
}
}
if (imagePath4 != null) {
final path = await _wearEngineHostApi.removeBackground(imagePath4);
if (path != null) {
imagePath4 = path;
}
}
final createdTheme = await _loadCreatedTheme();
Get.toNamed(Routes.WATCH_THEME_CUSTOM_PREVIEW, arguments: {
'theme': createdTheme ?? _buildThemeItem(),
});
//TODO: - 上传图片
// final result = await _themeApi.createTheme(
// name: customThemeName.value,
// fullOfEnergyName: customStatusNames[0],
// normalName: customStatusNames[1],
// overpressureName: customStatusNames[3],
// fullOfEnergyImage: customImagePaths[0] ?? '',
// normalImage: customImagePaths[1] ?? '',
// overpressureImage: customImagePaths[3] ?? customImagePaths[2] ?? '',
// );
isSaving.value = false;
//Test: - 测试抠图结果
customImagePaths.value = [imagePath1, imagePath2, imagePath3, imagePath4];
customImagePaths.refresh();
// final createdTheme = await _loadCreatedTheme();
// Get.toNamed(Routes.WATCH_THEME_CUSTOM_PREVIEW, arguments: {
// 'theme': createdTheme ?? _buildThemeItem(),
// });
}
WatchThemeItem _buildThemeItem() {
... ...
... ... @@ -258,14 +258,14 @@ class WearEngineHostApi {
/// Opens the system photo picker and returns a local PNG file path after
/// removing the image background on the host platform.
Future<String?> pickImageAndRemoveBackground() async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.WearEngineHostApi.pickImageAndRemoveBackground$pigeonVar_messageChannelSuffix';
Future<String?> removeBackground(String originImagePath) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.WearEngineHostApi.removeBackground$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[originImagePath]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
... ...
... ... @@ -40,7 +40,9 @@ abstract class WearEngineHostApi {
bool sendWatchSyncPayload(String jsonPayload);
/// Opens the system photo picker and returns a local PNG file path after
/// removing the image background on the host platform.
String? pickImageAndRemoveBackground();
@async
String? removeBackground(String originImagePath);
}
... ...