Commit c0004b571c3d2b6e6b9e2d0eda11613f7bc5881c

Authored by 权海
1 parent f5857469

feat(ui):新增分享文本和文件(图片等)的原生接口

... ... @@ -340,6 +340,8 @@ interface PlatformHostApi {
fun getFullUserAgent(): String
/** 是否是测试环境 */
fun isDebugEnvoriment(): Boolean
fun shareText(text: String, callback: (Result<Boolean>) -> Unit)
fun shareFileData(databytes: ByteArray, callback: (Result<Boolean>) -> Unit)
/**
* 更新用户信息, 有登录态后调用
* jsonString: UserPreferences的序列化string
... ... @@ -421,6 +423,46 @@ interface PlatformHostApi {
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareText$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val textArg = args[0] as String
api.shareText(textArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(PlatformApiPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(PlatformApiPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareFileData$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val databytesArg = args[0] as ByteArray
api.shareFileData(databytesArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(PlatformApiPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(PlatformApiPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.updateLoginInfo$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
... ...
... ... @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 77;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
... ...
... ... @@ -370,6 +370,8 @@ protocol PlatformHostApi {
func getFullUserAgent() throws -> String
/// 是否是测试环境
func isDebugEnvoriment() throws -> Bool
func shareText(text: String, completion: @escaping (Result<Bool, Error>) -> Void)
func shareFileData(databytes: FlutterStandardTypedData, completion: @escaping (Result<Bool, Error>) -> Void)
/// 更新用户信息, 有登录态后调用
/// jsonString: UserPreferences的序列化string
/// baseUrl: 请求地址, https://api.doublefeel.cn
... ... @@ -439,6 +441,40 @@ class PlatformHostApiSetup {
} else {
isDebugEnvorimentChannel.setMessageHandler(nil)
}
let shareTextChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareText\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
shareTextChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let textArg = args[0] as! String
api.shareText(text: textArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
shareTextChannel.setMessageHandler(nil)
}
let shareFileDataChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareFileData\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
shareFileDataChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let databytesArg = args[0] as! FlutterStandardTypedData
api.shareFileData(databytes: databytesArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
shareFileDataChannel.setMessageHandler(nil)
}
/// 更新用户信息, 有登录态后调用
/// jsonString: UserPreferences的序列化string
/// baseUrl: 请求地址, https://api.doublefeel.cn
... ...
... ... @@ -98,6 +98,26 @@ final class PlatformHostApiImpl: PlatformHostApi {
UIApplication.shared.open(url)
}
}
func shareText(text: String, completion: @escaping (Result<Bool, any Error>) -> Void) {
presentShareSheet(items: [text], completion: completion)
}
func shareFileData(databytes: FlutterStandardTypedData, completion: @escaping (Result<Bool, any Error>) -> Void) {
let fileURL = FileManager.default.temporaryDirectory
.appendingPathComponent("doublefeel-share-\(UUID().uuidString)")
.appendingPathExtension("data")
do {
try databytes.data.write(to: fileURL, options: .atomic)
} catch {
print("[PlatformHostApiImpl.shareFileData] return error: \(error)")
completion(.failure(error))
return
}
presentShareSheet(items: [fileURL], cleanupURL: fileURL, completion: completion)
}
func uploadFile(filePath: String, resourceType: HResourceType, completion: @escaping (Result<String?, any Error>) -> Void) {
let obsResourceType: HuaweiOBSResourceType
... ... @@ -274,3 +294,73 @@ final class PlatformHostApiImpl: PlatformHostApi {
return userAgent
}
}
//MARK: - 分享
extension PlatformHostApiImpl{
private func presentShareSheet(
items: [Any],
cleanupURL: URL? = nil,
completion: @escaping (Result<Bool, any Error>) -> Void
) {
DispatchQueue.main.async {
guard let viewController = Self.topViewController() else {
if let cleanupURL {
try? FileManager.default.removeItem(at: cleanupURL)
}
let error = NSError(
domain: "PlatformHostApiImpl.Share",
code: 1,
userInfo: [NSLocalizedDescriptionKey: "Unable to find a view controller for sharing."]
)
print("[PlatformHostApiImpl.share] return error: \(error)")
completion(.failure(error))
return
}
let activityController = UIActivityViewController(
activityItems: items,
applicationActivities: nil
)
activityController.popoverPresentationController?.sourceView = viewController.view
activityController.popoverPresentationController?.sourceRect = CGRect(
x: viewController.view.bounds.midX,
y: viewController.view.bounds.midY,
width: 1,
height: 1
)
activityController.completionWithItemsHandler = { _, completed, _, error in
if let cleanupURL {
try? FileManager.default.removeItem(at: cleanupURL)
}
if let error {
print("[PlatformHostApiImpl.share] return error: \(error)")
completion(.failure(error))
} else {
print("[PlatformHostApiImpl.share] return: \(completed)")
completion(.success(completed))
}
}
viewController.present(activityController, animated: true)
}
}
private static func topViewController(
from rootViewController: UIViewController? = UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.first(where: { $0.activationState == .foregroundActive })?
.windows
.first(where: { $0.isKeyWindow })?
.rootViewController
) -> UIViewController? {
if let presented = rootViewController?.presentedViewController {
return topViewController(from: presented)
}
if let navigationController = rootViewController as? UINavigationController {
return topViewController(from: navigationController.visibleViewController)
}
if let tabBarController = rootViewController as? UITabBarController {
return topViewController(from: tabBarController.selectedViewController)
}
return rootViewController
}
}
... ...
... ... @@ -9,6 +9,7 @@ import 'package:doublefeel_flutter/core/error/http_error_handling_policy.dart';
import 'package:doublefeel_flutter/core/network/api/friend_api.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/user_state_service.dart';
import 'package:doublefeel_flutter/core/util/app_toast.dart';
import 'package:doublefeel_flutter/data/local/user_preferences_storage.dart';
import 'package:doublefeel_flutter/data/models/friend/friend_models.dart';
import 'package:doublefeel_flutter/l10n/l10n_extensions.dart';
... ... @@ -64,11 +65,16 @@ class BindPartnerController extends GetxController {
bool get canSubmit => partnerIdInput.value.isNotEmpty;
void onShareMyCode() {
Share.share("""嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!
void onShareMyCode() async {
try {
final success =
await PlatformHostApi().shareText("""嘿 ❤️ 我发现了一个叫 DoubleFeel 的 App!
它可以帮我们互相关心 — 记录压力和睡眠、查看 HRV 与身体状态、及时了解彼此的健康状况。
快来一起使用吧 👉 https://doublefeel.cn/
我的好友id:${myInviteCode.value}""");
} catch (e) {
AppToast.show(e.toString());
}
}
Future<void> onSubmitPartnerId() async {
... ...
... ... @@ -422,6 +422,62 @@ class PlatformHostApi {
}
}
Future<bool> shareText(String text) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareText$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[text]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as bool?)!;
}
}
Future<bool> shareFileData(Uint8List databytes) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.doublefeel_flutter.PlatformHostApi.shareFileData$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[databytes]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as bool?)!;
}
}
/// 更新用户信息, 有登录态后调用
/// jsonString: UserPreferences的序列化string
/// baseUrl: 请求地址, https://api.doublefeel.cn
... ...
... ... @@ -119,6 +119,11 @@ abstract class PlatformHostApi {
/// 是否是测试环境
bool isDebugEnvoriment();
@async
bool shareText(String text);
@async
bool shareFileData(Uint8List databytes);
/// 更新用户信息, 有登录态后调用
/// jsonString: UserPreferences的序列化string
/// baseUrl: 请求地址, https://api.doublefeel.cn
... ...