HarmonyOS next之harmony_flutter 支持分享文本、图片、视频和文件
ohos端适配
import Log from '@ohos/flutter_ohos/src/main/ets/util/Log';
import UIAbility from '@ohos.app.ability.UIAbility';
import List from '@ohos.util.List';
import fs from '@ohos.file.fs';
import { fileUri } from '@kit.CoreFileKit';
import { MethodResult } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import systemShare from '@hms.collaboration.systemShare';
import uniformTypeDescriptor from '@ohos.data.uniformTypeDescriptor';
const TAG = 'FlutterShare';
export default class Share {
private ability?: UIAbility | undefined = undefined;
constructor() {
}
public setAbility(ability: UIAbility | undefined): void {
this.ability = ability;
}
/**
* 分享文本
*
* @param text 待分享文本
* @param withResult 是否需要dismiss回调
* @param result result
*/
public shareText(text: string, withResult?: boolean, result?: MethodResult): void {
if (!this.ability) {
Log.w(TAG, "share ability not exist");
return
}
Log.i(TAG, "share");
let record: systemShare.SharedRecord = {
utd: uniformTypeDescriptor.getUniformDataTypeByMIMEType('text/plain'),
content: text || ''
}
let data = new systemShare.SharedData(record);
this.startShare(data, withResult, result);
}
/**
* 分享文件
*
* @param paths 待分享文件路径集合
* @param subject 分享标题
* @param withResult 是否需要dismiss回调
* @param result result
* @returns await
*/
public async shareFiles(paths: List<string>, subject: string | null, withResult?: boolean, result?: MethodResult): Promise<void> {
if (!this.ability) {
Log.w(TAG, "shareFiles ability not exist");
return
}
Log.i(TAG, "shareFiles");
await this.clearShareCache()
let fileUris = await this.getUrisForPaths(paths)
Log.d(TAG, "shareFiles: path->" + JSON.stringify(fileUris));
if (!fileUris || fileUris.length == 0) {
Log.w(TAG, "shareFiles copy to cache failed");
return
}
let data: systemShare.SharedData | null = null;
fileUris.forEach((uri: string, index: number) => {
let record: systemShare.SharedRecord = {
utd: this.getUniformTypeDescriptor(uri),
uri: uri,
title: subject || ''
}
if (data) {
data.addRecord(record);
} else {
data = new systemShare.SharedData(record);
}
})
this.startShare(data, withResult, result);
}
private startShare(data: systemShare.SharedData | null, withResult?: boolean, result?: MethodResult): void {
if (!this.ability || !data) {
Log.w(TAG, "startShare ability not exist or data empty");
return
}
Log.i(TAG, "startShare");
try {
let controller = new systemShare.ShareController(data);
if (withResult) {
controller.on('dismiss', () => {
result?.success('');
});
}
controller.show(this.ability!.context, {
previewMode: systemShare.SharePreviewMode.DETAIL,
selectionMode: systemShare.SelectionMode.BATCH
});
} catch (err) {
Log.e(TAG, 'startShare err:' + JSON.stringify(err));
}
}
private getUniformTypeDescriptor(fileUri: string): string {
const dotIndex = fileUri.lastIndexOf('.');
if (dotIndex != -1) {
const fileExtension = '.' + fileUri.substring(dotIndex + 1);
return uniformTypeDescriptor.getUniformDataTypeByFilenameExtension(fileExtension) || 'general.file';
}
return 'general.file';
}
/**
* 分享路径
*
* @returns 分享路径
*/
private shareCacheFolder(): string {
if (!this.ability) {
Log.w(TAG, "shareCacheFolder ability not exist");
return ""
}
return this.ability.context.cacheDir + '/share_extend' || "";
}
/**
* 获取复制到cache后的路径集合
*
* @param paths paths
* @returns cache的路径集合
*/
private async getUrisForPaths(paths: List<string>): Promise<Array<string>> {
Log.i(TAG, "getUrisForPaths");
let uris = new Array<string>();
try {
paths.forEach(async path => {
let file: string = await this.copyToShareCache(path);
if (file) {
uris.push(fileUri.getUriFromPath(file));
}
})
} catch (err) {
Log.e(TAG, 'getUrisForPaths err:' + JSON.stringify(err));
}
return uris
}
/**
* 清空cache缓存
*
* @returns 清空cache缓存
*/
private async clearShareCache(): Promise<void> {
Log.i(TAG, "clearShareCacheFolder");
try {
let folder: string = this.shareCacheFolder();
if (folder && fs.accessSync(folder)) {
let files = fs.listFileSync(folder);
if (files.length > 0) {
files.forEach(item => {
fs.unlinkSync(folder + '/' + item);
})
fs.rmdirSync(folder);
}
}
} catch (err) {
Log.e(TAG, 'clearShareCacheFolder err:' + JSON.stringify(err));
}
}
/**
* 复制到缓存目录
*
* @param file 待复制文件
* @returns 缓存路径
*/
private async copyToShareCache(file: string): Promise<string> {
let folder = this.shareCacheFolder();
if (!folder) {
return ""
}
Log.i(TAG, "copyToShareCache");
if (!fs.accessSync(folder)) {
fs.mkdirSync(folder);
}
let oldFile: fs.File = fs.openSync(file);
let newFile = folder + '/' + oldFile.name;
fs.copyFileSync(oldFile.fd, newFile);
return newFile
}
}
import {
FlutterPlugin,
FlutterPluginBinding
} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import MethodChannel from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import { AbilityAware, AbilityPluginBinding, Log } from '@ohos/flutter_ohos';
import MethodChannelHandlerImpl from './MethodChannelHandlerImpl';
import Share from './Share';
const TAG = "ShareExtendPlugin";
export default class ShareExtendPlugin implements FlutterPlugin, AbilityAware {
private channel: MethodChannel | null = null;
private methodChannelHandlerImpl: MethodChannelHandlerImpl | null = null;
private share: Share | null = null
constructor() {
}
getUniqueClassName(): string {
return "ShareExtendPlugin"
}
onAttachedToAbility(binding: AbilityPluginBinding) {
Log.i(TAG, "onAttachedToAbility");
this.share?.setAbility(binding.getAbility());
}
onDetachedFromAbility() {
Log.i(TAG, "onDetachedFromAbility");
this.share?.setAbility(undefined);
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.share = new Share();
this.methodChannelHandlerImpl = new MethodChannelHandlerImpl(this.share);
this.channel = new MethodChannel(binding.getBinaryMessenger(), "com.zt.shareextend/share_extend");
this.channel.setMethodCallHandler(this.methodChannelHandlerImpl);
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
if (this.channel != null) {
this.channel.setMethodCallHandler(null)
this.channel = null;
}
}
}
import Log from '@ohos/flutter_ohos/src/main/ets/util/Log';
import { MethodCall } from '@ohos/flutter_ohos';
import { MethodCallHandler, MethodResult } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import Share from './Share';
import { List } from '@kit.ArkTS';
const TAG = 'MethodCallHandlerImpl';
export default class MethodCallHandlerImpl implements MethodCallHandler {
private share: Share | null = null;
constructor(share: Share) {
this.share = share;
}
onMethodCall(call: MethodCall, result: MethodResult): void {
let method: string = call.method;
Log.i(TAG, "onMethodCall: " + method);
let type: string = call.argument("type");
let list: List<string> = call.argument("list");
let subject: string = call.argument('subject');
try {
switch (method) {
case 'share':
if ("text" === type) {
this.share?.shareText(list[0]);
} else {
this.share?.shareFiles(list, subject);
}
break;
default:
result.notImplemented();
break;
}
} catch (err) {
Log.e(TAG, 'err:' + JSON.stringify(err));
}
}
}
import 'dart:async';
import 'package:flutter/services.dart';
import 'dart:ui';
class ShareExtend {
static const MethodChannel _channel =
const MethodChannel('com.zt.shareextend/share_extend');
static Future<void> shareMultiple(List<String> list, String type,
{Rect? sharePositionOrigin,
String? sharePanelTitle,
String subject = "",
List<String>? extraTexts}) {
assert(list.isNotEmpty);
return _shareInner(list, type,
sharePositionOrigin: sharePositionOrigin,
subject: subject,
sharePanelTitle: sharePanelTitle,
extraTexts: extraTexts);
}
/// method to share with system ui
/// It uses the ACTION_SEND Intent on Android and UIActivityViewController
/// on iOS.
/// [list] can be text or path list
/// [type] "text", "image", "audio", "video" or "file"
/// [sharePositionOrigin] only supports iPad os
/// [sharePanelTitle] only supports android (some devices may not support)
/// [subject] Intent.EXTRA_SUBJECT on Android and "subject" on iOS.
/// [extraText] only supports android for Intent.EXTRA_TEXT when sharing image or file.
///
static Future<void> share(String text, String type,
{Rect? sharePositionOrigin,
String? sharePanelTitle,
String subject = "",
String extraText = ""}) {
assert(text.isNotEmpty);
List<String> list = [text];
return _shareInner(
list,
type,
sharePositionOrigin: sharePositionOrigin,
sharePanelTitle: sharePanelTitle,
subject: subject,
extraTexts: [extraText],
);
}
static Future<void> _shareInner(List<String> list, String type,
{Rect? sharePositionOrigin,
String? sharePanelTitle,
String? subject,
List<String>? extraTexts}) {
assert(list.isNotEmpty);
final Map<String, dynamic> params = <String, dynamic>{
'list': list,
'type': type,
'sharePanelTitle': sharePanelTitle,
'subject': subject,
'extraTexts': extraTexts
};
if (sharePositionOrigin != null) {
params['originX'] = sharePositionOrigin.left;
params['originY'] = sharePositionOrigin.top;
params['originWidth'] = sharePositionOrigin.width;
params['originHeight'] = sharePositionOrigin.height;
}
return _channel.invokeMethod('share', params);
}
}
flutter端代分享类代码封装
import 'dart:async';
import 'package:flutter/services.dart';
import 'dart:ui';
/// Plugin for summoning a platform share sheet.
class ShareExtend {
/// [MethodChannel] used to communicate with the platform side.
static const MethodChannel _channel =
const MethodChannel('com.zt.shareextend/share_extend');
/// method to share with system ui
/// It uses the ACTION_SEND Intent on Android and UIActivityViewController
/// on iOS.
/// [list] can be text or path list
/// [type] "text", "image", "audio", "video" or "file"
/// [sharePositionOrigin] only supports iPad os
/// [sharePanelTitle] only supports android (some devices may not support)
/// [subject] Intent.EXTRA_SUBJECT on Android and "subject" on iOS.
/// [extraText] only supports android for Intent.EXTRA_TEXT when sharing image or file.
///
static Future<void> shareMultiple(List<String> list, String type,
{Rect? sharePositionOrigin,
String? sharePanelTitle,
String subject = "",
List<String>? extraTexts}) {
assert(list.isNotEmpty);
return _shareInner(list, type,
sharePositionOrigin: sharePositionOrigin,
subject: subject,
sharePanelTitle: sharePanelTitle,
extraTexts: extraTexts);
}
/// method to share with system ui
/// It uses the ACTION_SEND Intent on Android and UIActivityViewController
/// on iOS.
/// [list] can be text or path list
/// [type] "text", "image", "audio", "video" or "file"
/// [sharePositionOrigin] only supports iPad os
/// [sharePanelTitle] only supports android (some devices may not support)
/// [subject] Intent.EXTRA_SUBJECT on Android and "subject" on iOS.
/// [extraText] only supports android for Intent.EXTRA_TEXT when sharing image or file.
///
static Future<void> share(String text, String type,
{Rect? sharePositionOrigin,
String? sharePanelTitle,
String subject = "",
String extraText = ""}) {
assert(text.isNotEmpty);
List<String> list = [text];
return _shareInner(
list,
type,
sharePositionOrigin: sharePositionOrigin,
sharePanelTitle: sharePanelTitle,
subject: subject,
extraTexts: [extraText],
);
}
static Future<void> _shareInner(List<String> list, String type,
{Rect? sharePositionOrigin,
String? sharePanelTitle,
String? subject,
List<String>? extraTexts}) {
assert(list.isNotEmpty);
final Map<String, dynamic> params = <String, dynamic>{
'list': list,
'type': type,
'sharePanelTitle': sharePanelTitle,
'subject': subject,
'extraTexts': extraTexts
};
if (sharePositionOrigin != null) {
params['originX'] = sharePositionOrigin.left;
params['originY'] = sharePositionOrigin.top;
params['originWidth'] = sharePositionOrigin.width;
params['originHeight'] = sharePositionOrigin.height;
}
return _channel.invokeMethod('share', params);
}
}
这样就能通过调用ShareExtend中对应的方法来实现分享功能.