|
|
@@ -0,0 +1,461 @@
|
|
|
+import Intent from "android.content.Intent";
|
|
|
+import Uri from "android.net.Uri";
|
|
|
+import Context from "android.content.Context";
|
|
|
+import File from "java.io.File";
|
|
|
+import FileProvider from "androidx.core.content.FileProvider";
|
|
|
+import { ShareWithSystemOptions } from "../interface.uts";
|
|
|
+
|
|
|
+/**
|
|
|
+ * 分享类型枚举
|
|
|
+ */
|
|
|
+const ShareType = {
|
|
|
+ TEXT: "text", // 纯文本分享
|
|
|
+ IMAGE: "image", // 图片分享
|
|
|
+ VIDEO: "video", // 视频分享
|
|
|
+ AUDIO: "audio", // 音频分享
|
|
|
+ FILE: "file", // 文件分享
|
|
|
+ LINK: "link" // 链接分享
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * MIME 类型映射
|
|
|
+ */
|
|
|
+const MimeTypes = {
|
|
|
+ IMAGE: "image/*",
|
|
|
+ VIDEO: "video/*",
|
|
|
+ AUDIO: "audio/*",
|
|
|
+ TEXT: "text/plain",
|
|
|
+ PDF: "application/pdf",
|
|
|
+ WORD: "application/msword",
|
|
|
+ EXCEL: "application/vnd.ms-excel",
|
|
|
+ PPT: "application/vnd.ms-powerpoint",
|
|
|
+ ZIP: "application/zip",
|
|
|
+ DEFAULT: "*/*"
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 判断是否为网络 URL
|
|
|
+ * @param url 地址
|
|
|
+ * @returns 是否为网络 URL
|
|
|
+ */
|
|
|
+function isNetworkUrl(url: string): boolean {
|
|
|
+ return url.startsWith("http://") || url.startsWith("https://");
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 根据文件路径获取 File 对象
|
|
|
+ * 按优先级尝试多种路径解析方式
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @returns File 对象或 null
|
|
|
+ */
|
|
|
+function getFileFromPath(filePath: string): File | null {
|
|
|
+ // 1. 尝试直接路径
|
|
|
+ let file = new File(filePath);
|
|
|
+ if (file.exists()) {
|
|
|
+ return file;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 尝试资源路径
|
|
|
+ file = new File(UTSAndroid.getResourcePath(filePath));
|
|
|
+ if (file.exists()) {
|
|
|
+ return file;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 尝试绝对路径转换
|
|
|
+ file = new File(UTSAndroid.convert2AbsFullPath(filePath));
|
|
|
+ if (file.exists()) {
|
|
|
+ return file;
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 根据文件扩展名获取 MIME 类型
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @param defaultType 默认类型
|
|
|
+ * @returns MIME 类型字符串
|
|
|
+ */
|
|
|
+function getMimeTypeByPath(filePath: string, defaultType: string): string {
|
|
|
+ const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
|
|
|
+
|
|
|
+ if (ext == "") {
|
|
|
+ return defaultType;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 常见文件类型映射
|
|
|
+ const mimeMap = {
|
|
|
+ // 文档类型
|
|
|
+ pdf: MimeTypes["PDF"],
|
|
|
+ doc: MimeTypes["WORD"],
|
|
|
+ docx: MimeTypes["WORD"],
|
|
|
+ xls: MimeTypes["EXCEL"],
|
|
|
+ xlsx: MimeTypes["EXCEL"],
|
|
|
+ ppt: MimeTypes["PPT"],
|
|
|
+ pptx: MimeTypes["PPT"],
|
|
|
+ // 压缩包类型
|
|
|
+ zip: MimeTypes["ZIP"],
|
|
|
+ rar: "application/x-rar-compressed",
|
|
|
+ "7z": "application/x-7z-compressed",
|
|
|
+ tar: "application/x-tar",
|
|
|
+ gz: "application/gzip"
|
|
|
+ };
|
|
|
+
|
|
|
+ return (mimeMap[ext] as string) ?? defaultType;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 下载网络文件到本地缓存
|
|
|
+ * @param url 网络地址
|
|
|
+ * @param success 成功回调,返回本地文件路径
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function downloadNetworkFile(
|
|
|
+ url: string,
|
|
|
+ success: (localPath: string) => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ uni.downloadFile({
|
|
|
+ url: url,
|
|
|
+ success: (res) => {
|
|
|
+ if (res.statusCode == 200) {
|
|
|
+ success(res.tempFilePath);
|
|
|
+ } else {
|
|
|
+ fail("下载失败,状态码: " + res.statusCode);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ fail("下载失败: " + (err.errMsg ?? "未知错误"));
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建文件 Uri
|
|
|
+ * @param filePath 文件路径(支持本地路径和网络 URL)
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function createFileUriAsync(
|
|
|
+ filePath: string,
|
|
|
+ success: (uri: Uri) => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ // 创建文件Uri,支持网络和本地文件。网络文件先下载到本地缓存,再获取Uri。
|
|
|
+ const handleFileToUri = (localPath: string) => {
|
|
|
+ const file = getFileFromPath(localPath);
|
|
|
+ if (file == null) {
|
|
|
+ fail(`文件不存在: ${localPath}`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const context = UTSAndroid.getAppContext();
|
|
|
+ if (context == null) {
|
|
|
+ fail("无法获取App Context");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const authority = context.getPackageName() + ".fileprovider";
|
|
|
+ const uri = FileProvider.getUriForFile(context, authority, file);
|
|
|
+ success(uri);
|
|
|
+ };
|
|
|
+
|
|
|
+ if (isNetworkUrl(filePath)) {
|
|
|
+ // 网络路径需先下载,下载完成后处理
|
|
|
+ downloadNetworkFile(filePath, handleFileToUri, fail);
|
|
|
+ } else {
|
|
|
+ // 本地文件直接处理
|
|
|
+ handleFileToUri(filePath);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建图片分享 Intent(异步)
|
|
|
+ * @param imageUrl 图片路径(支持本地路径和网络 URL)
|
|
|
+ * @param title 分享标题
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function createImageShareIntent(
|
|
|
+ imageUrl: string,
|
|
|
+ title: string,
|
|
|
+ success: (intent: Intent) => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ if (imageUrl == "") {
|
|
|
+ fail("图片路径不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ createFileUriAsync(
|
|
|
+ imageUrl,
|
|
|
+ (uri: Uri) => {
|
|
|
+ const intent = new Intent(Intent.ACTION_SEND);
|
|
|
+ intent.setType(MimeTypes["IMAGE"] as string);
|
|
|
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
|
|
|
+ intent.putExtra(Intent.EXTRA_TITLE, title);
|
|
|
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
+ success(intent);
|
|
|
+ },
|
|
|
+ (error: string) => {
|
|
|
+ fail(error);
|
|
|
+ }
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建视频分享 Intent(异步)
|
|
|
+ * @param videoUrl 视频路径(支持本地路径和网络 URL)
|
|
|
+ * @param title 分享标题
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function createVideoShareIntent(
|
|
|
+ videoUrl: string,
|
|
|
+ title: string,
|
|
|
+ success: (intent: Intent) => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ if (videoUrl == "") {
|
|
|
+ fail("视频路径不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ createFileUriAsync(
|
|
|
+ videoUrl,
|
|
|
+ (uri: Uri) => {
|
|
|
+ const intent = new Intent(Intent.ACTION_SEND);
|
|
|
+ intent.setType(MimeTypes["VIDEO"] as string);
|
|
|
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
|
|
|
+ intent.putExtra(Intent.EXTRA_TITLE, title);
|
|
|
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
+ success(intent);
|
|
|
+ },
|
|
|
+ (error: string) => {
|
|
|
+ fail(error);
|
|
|
+ }
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建音频分享 Intent(异步)
|
|
|
+ * @param audioUrl 音频路径(支持本地路径和网络 URL)
|
|
|
+ * @param title 分享标题
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function createAudioShareIntent(
|
|
|
+ audioUrl: string,
|
|
|
+ title: string,
|
|
|
+ success: (intent: Intent) => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ if (audioUrl == "") {
|
|
|
+ fail("音频路径不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ createFileUriAsync(
|
|
|
+ audioUrl,
|
|
|
+ (uri: Uri) => {
|
|
|
+ const intent = new Intent(Intent.ACTION_SEND);
|
|
|
+ intent.setType(MimeTypes["AUDIO"] as string);
|
|
|
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
|
|
|
+ intent.putExtra(Intent.EXTRA_TITLE, title);
|
|
|
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
+ success(intent);
|
|
|
+ },
|
|
|
+ (error: string) => {
|
|
|
+ fail(error);
|
|
|
+ }
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建文件分享 Intent(异步)
|
|
|
+ * @param filePath 文件路径(支持本地路径和网络 URL)
|
|
|
+ * @param title 分享标题
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function createFileShareIntent(
|
|
|
+ filePath: string,
|
|
|
+ title: string,
|
|
|
+ success: (intent: Intent) => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ if (filePath == "") {
|
|
|
+ fail("文件路径不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ createFileUriAsync(
|
|
|
+ filePath,
|
|
|
+ (uri: Uri) => {
|
|
|
+ // 根据文件扩展名确定 MIME 类型
|
|
|
+ const mimeType = getMimeTypeByPath(filePath, MimeTypes["DEFAULT"] as string);
|
|
|
+
|
|
|
+ const intent = new Intent(Intent.ACTION_SEND);
|
|
|
+ intent.setType(mimeType);
|
|
|
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
|
|
|
+ intent.putExtra(Intent.EXTRA_TITLE, title);
|
|
|
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
+ success(intent);
|
|
|
+ },
|
|
|
+ (error: string) => {
|
|
|
+ fail(error);
|
|
|
+ }
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建链接分享 Intent
|
|
|
+ * @param href 链接地址
|
|
|
+ * @param title 分享标题
|
|
|
+ * @param summary 分享描述
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function createLinkShareIntent(
|
|
|
+ href: string,
|
|
|
+ title: string,
|
|
|
+ summary: string,
|
|
|
+ success: (intent: Intent) => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ if (href == "") {
|
|
|
+ fail("链接地址不能为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 组合分享内容:标题 + 描述 + 链接
|
|
|
+ let content = "";
|
|
|
+ if (title != "") {
|
|
|
+ content = title;
|
|
|
+ }
|
|
|
+ if (summary != "") {
|
|
|
+ content = content == "" ? summary : content + "\n" + summary;
|
|
|
+ }
|
|
|
+ if (href != "") {
|
|
|
+ content = content == "" ? href : content + "\n" + href;
|
|
|
+ }
|
|
|
+
|
|
|
+ const intent = new Intent(Intent.ACTION_SEND);
|
|
|
+ intent.setType(MimeTypes["TEXT"] as string);
|
|
|
+ intent.putExtra(Intent.EXTRA_TEXT, content);
|
|
|
+
|
|
|
+ success(intent);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 创建文本分享 Intent
|
|
|
+ * @param title 分享标题
|
|
|
+ * @param summary 分享描述
|
|
|
+ * @param href 附加链接(可选)
|
|
|
+ * @param success 成功回调
|
|
|
+ */
|
|
|
+function createTextShareIntent(
|
|
|
+ title: string,
|
|
|
+ summary: string,
|
|
|
+ href: string,
|
|
|
+ success: (intent: Intent) => void
|
|
|
+): void {
|
|
|
+ // 组合分享内容
|
|
|
+ let content = "";
|
|
|
+ if (title != "") {
|
|
|
+ content = title;
|
|
|
+ }
|
|
|
+ if (summary != "") {
|
|
|
+ content = content == "" ? summary : content + "\n" + summary;
|
|
|
+ }
|
|
|
+ if (href != "") {
|
|
|
+ content = content == "" ? href : content + "\n" + href;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果内容为空,使用默认文本
|
|
|
+ if (content == "") {
|
|
|
+ content = "分享内容";
|
|
|
+ }
|
|
|
+
|
|
|
+ const intent = new Intent(Intent.ACTION_SEND);
|
|
|
+ intent.setType(MimeTypes["TEXT"] as string);
|
|
|
+ intent.putExtra(Intent.EXTRA_TEXT, content);
|
|
|
+
|
|
|
+ success(intent);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 启动分享 Activity
|
|
|
+ * @param intent 分享 Intent
|
|
|
+ * @param title 选择器标题
|
|
|
+ * @param success 成功回调
|
|
|
+ * @param fail 失败回调
|
|
|
+ */
|
|
|
+function startShareActivity(
|
|
|
+ intent: Intent,
|
|
|
+ title: string,
|
|
|
+ success: () => void,
|
|
|
+ fail: (error: string) => void
|
|
|
+): void {
|
|
|
+ const chooserTitle = title != "" ? title : "选择分享方式";
|
|
|
+ const chooser = Intent.createChooser(intent, chooserTitle);
|
|
|
+
|
|
|
+ try {
|
|
|
+ UTSAndroid.getUniActivity()!.startActivity(chooser);
|
|
|
+ success();
|
|
|
+ } catch (e: Exception) {
|
|
|
+ const errorMsg = e.message ?? "分享失败";
|
|
|
+ fail(errorMsg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 系统分享功能
|
|
|
+ * @param options 分享参数
|
|
|
+ * @param options.type 分享类型: text(文本) | image(图片) | video(视频) | audio(音频) | file(文件) | link(链接)
|
|
|
+ * @param options.title 分享标题
|
|
|
+ * @param options.summary 分享描述/内容
|
|
|
+ * @param options.href 链接地址或文件路径
|
|
|
+ * @param options.imageUrl 图片/视频/音频路径(支持本地路径和网络 URL)
|
|
|
+ * @param options.success 成功回调
|
|
|
+ * @param options.fail 失败回调
|
|
|
+ */
|
|
|
+export function shareWithSystem(options: ShareWithSystemOptions): void {
|
|
|
+ const type = options.type;
|
|
|
+ const title = options.title ?? "";
|
|
|
+ const summary = options.summary ?? "";
|
|
|
+ const href = options.href ?? "";
|
|
|
+ const imageUrl = options.imageUrl ?? "";
|
|
|
+
|
|
|
+ // 成功和失败回调
|
|
|
+ const onSuccess = (intent: Intent) => {
|
|
|
+ startShareActivity(
|
|
|
+ intent,
|
|
|
+ title,
|
|
|
+ () => {
|
|
|
+ options.success?.();
|
|
|
+ },
|
|
|
+ (error: string) => {
|
|
|
+ options.fail?.(error);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ const onFail = (error: string) => {
|
|
|
+ options.fail?.(error);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 根据分享类型创建对应的 Intent
|
|
|
+ if (type == ShareType["IMAGE"]) {
|
|
|
+ createImageShareIntent(imageUrl, title, onSuccess, onFail);
|
|
|
+ } else if (type == ShareType["VIDEO"]) {
|
|
|
+ createVideoShareIntent(imageUrl, title, onSuccess, onFail);
|
|
|
+ } else if (type == ShareType["AUDIO"]) {
|
|
|
+ createAudioShareIntent(imageUrl, title, onSuccess, onFail);
|
|
|
+ } else if (type == ShareType["FILE"]) {
|
|
|
+ createFileShareIntent(href, title, onSuccess, onFail);
|
|
|
+ } else if (type == ShareType["LINK"]) {
|
|
|
+ createLinkShareIntent(href, title, summary, onSuccess, onFail);
|
|
|
+ } else {
|
|
|
+ // 默认为文本分享
|
|
|
+ createTextShareIntent(title, summary, href, onSuccess);
|
|
|
+ }
|
|
|
+}
|