compose调用系统分享功能分享图片文件

compose调用系统分享功能图片文件

  • 简介
    • UI界面
    • 提供给外部程序的文件访问权限
      • 创建FileProvider
      • 设置共享文件夹
  • 通用分享工具
  • 虚拟机验证结果
  • 参考

本系列用于新人安卓基础入门学习笔记,有任何不同的见解欢迎留言

运行环境 jdk17 andriod 34 compose material3

简介

本案例采用 provider来分享当前应用下的文件,其他系统文件直接通过context地址直接获取
本案例是直接 【MediaProvider】content://media/external/images/media,来让其他app直接访问,如果是系统文件请直接忽略provider相关设置

在这里插入图片描述

UI界面

在这里插入图片描述

package com.example.myapplication.uiimport android.app.Activity
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import com.example.myapplication.R
import com.example.myapplication.common.ShareUtil
import com.example.myapplication.common.Utils
import com.example.myapplication.common.ui.FullScreenImage
import com.example.myapplication.entity.ImageEntity
import java.io.Filevar snackbarHostState = SnackbarHostState()@Composable
fun ImageDetail(imageEntity: ImageEntity, mainController: NavHostController) {Scaffold(snackbarHost = {SnackbarHost(hostState = snackbarHostState, modifier = Modifier.padding(0.dp))},topBar = {ImageTopBar(imageEntity.name, mainController)},bottomBar = {GetBottomBar(imageEntity.file)}) { innerPadding ->FullScreenImage(imageEntity = imageEntity, modifier = Modifier.padding(innerPadding))}
}@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GetBottomBar(file: File) {val scope = rememberCoroutineScope()val activity = LocalContext.current as ActivityBottomAppBar(containerColor = MaterialTheme.colorScheme.primaryContainer,contentColor = MaterialTheme.colorScheme.primary,) {Row(Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceEvenly,verticalAlignment = Alignment.CenterVertically) {val buttonModifier = Modifier.size(70.dp)val IconModifier = Modifier.size(30.dp)val message = stringResource(id = R.string.empty_ui)IconButton(onClick = { Utils.message(scope, message, snackbarHostState) },modifier = buttonModifier) {Column(horizontalAlignment = Alignment.CenterHorizontally) {Icon(modifier = IconModifier,imageVector = Icons.Filled.Edit,contentDescription = "Localized description")Text(text = "编辑",fontSize = 12.sp,)}}IconButton(onClick = {// 打开系统分享ShareUtil.shareImage(activity,"com.example.myapplication.fileprovider",file.name,file.path)},modifier = buttonModifier) {Column(horizontalAlignment = Alignment.CenterHorizontally) {Icon(modifier = IconModifier,imageVector = Icons.Filled.Share,contentDescription = "Localized description")Text(text = "分享", fontSize = 12.sp)}}IconButton(onClick = { Utils.message(scope, message, snackbarHostState) },modifier = buttonModifier) {Column(horizontalAlignment = Alignment.CenterHorizontally) {Icon(modifier = IconModifier,imageVector = Icons.Filled.Delete,contentDescription = "Localized description")Text(text = "删除", fontSize = 12.sp)}}IconButton(onClick = { Utils.message(scope, message, snackbarHostState) },modifier = buttonModifier) {Column(horizontalAlignment = Alignment.CenterHorizontally) {Icon(modifier = IconModifier,imageVector = Icons.Filled.MoreVert,contentDescription = "Localized description")Text(text = "更多", fontSize = 12.sp)}}}}}
}

提供给外部程序的文件访问权限

  • 这里只适用于外部访问当前app下的数据,本案例是直接 content://media/external/images/media,来让其他app直接访问,如果是系统文件请直接忽略这一段
  • Android 7.0之前,文件的Uri以file:///形式提供给其他app访问。
  • Android 7.0之后,分享文件的Uri发生了变化。为了安全起见,file:///形式的Uri不能正常访问。官方提供了FileProvider,FileProvider生成的Uri会以content://的形式分享给其他app使用。
    content形式的Uri可以让其他app临时获得读取(Read)和写入(Write)权限,只要我们在创建Intent时,使用Intent.setFlags()添加权限。只要接收Uri的app在接收的Activity任务栈中处于活动状态,添加的权限就会一直有效,直到app被任务栈移除

创建FileProvider

<application>
....<providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.example.myapplication.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/filepath" /> </provider>
</application>
  • android:authorities 指定可以查找此内容提供程序的权限。可以使用分号分隔多个授权机构。授权机构名称应该使用java风格的命名约定(如com.google.provider.MyProvider),以避免冲突。通常,此名称与描述提供程序数据结构的类实现相同。
  • android:resource 文件的权限清单
  • android:exported 设置为false,FileProvider不需要公开。
  • android:grantUriPermissions 设置为true,这样就能授权接收端的app临时访问权限了。

设置共享文件夹

这里的绝对路径是 /data/data/{package}
例如我的是 /data/data/com.example.myapplication

在res/xml中创建一个资源文件filepath.xml

<?xml version="1.0" encoding="utf-8"?>
<paths><files-path name="Pictures" path="/pictures/"/>
</paths>

/data/data/com.example.myapplication/files/pictures/

通用分享工具

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;import androidx.core.content.FileProvider;import java.io.File;public class ShareUtil {// 原生通用分享文本public static void shareText(Activity activity, String title, String text) {Intent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);sendIntent.putExtra(Intent.EXTRA_TEXT, text);sendIntent.setType("text/plain");activity.startActivityForResult(Intent.createChooser(sendIntent, title), 80001);}// 原生通用分享图片public static void shareImage(Activity activity, String authority, String title, File file){shareImage(activity, authority, title, file, false);}public static void shareImage(Activity activity, String authority, String title, File file,  boolean isApp) {Intent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);Uri uri = getFileUri(activity, authority, file, isApp);sendIntent.putExtra(Intent.EXTRA_STREAM, uri);sendIntent.setType("image/png");activity.startActivityForResult(Intent.createChooser(sendIntent, title), 80002);}// 通用文件public static void shareFile(Activity activity, String authority, String type, File file,  boolean isApp) {Intent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);Uri uri = FileProvider.getUriForFile(activity, authority , file);sendIntent.putExtra(Intent.EXTRA_STREAM, uri);sendIntent.setType(type);activity.startActivityForResult(Intent.createChooser(sendIntent, file.getName()), 80002);}public static Uri getFileUri(Context context, String authority, File file, boolean isApp) {Uri uri;// 低版本直接用 Uri.fromFileif (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {uri = Uri.fromFile(file);} else {if(isApp){// 分享当前应用下的共享路径中的uri = FileProvider.getUriForFile(context, authority , file);}else {// 分享外部的文件uri = getImageContentUri(context, file);}}return uri;}public static Uri getImageContentUri(Context context, File imageFile) {String filePath = imageFile.getAbsolutePath();Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DATA + "=? ",new String[]{filePath}, null);if (cursor != null && cursor.moveToFirst()) {@SuppressLint("Range") int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));Uri baseUri = Uri.parse("content://media/external/images/media");return Uri.withAppendedPath(baseUri, "" + id);} else {if (imageFile.exists()) {ContentValues values = new ContentValues();values.put(MediaStore.Images.Media.DATA, filePath);return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);} else {return null;}}}
}

虚拟机验证结果

在这里插入图片描述
在这里插入图片描述

参考

Android之FileProvider详解 - 掘金 (juejin.cn)
Android原生分享与指定app分享_android 原生分享链接-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1410088.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

nginx的前世今生(二)

书接上回&#xff1a; 上回书说到&#xff0c;nginx的前世今生&#xff0c;这回我们继续说 3.缓冲秘籍&#xff0c;洪流控水 Nginx的缓冲区是其处理数据传输和提高性能的关键设计之一&#xff0c;主要用于暂存和管理进出的数据流&#xff0c;以应对不同组件间速度不匹配的问题…

【JVM】class文件格式,JVM加载class文件流程,JVM运行时内存区域,对象分配内存流程

这篇文章本来只是想讲一下class文件格式&#xff0c;讲着讲着越讲越多。JVM这一块吧&#xff0c;知识比较散比较多&#xff0c;如果深研究下去如死扣《深入理解Java虚拟机》&#xff0c;这本书很深很细&#xff0c;全记住是不可能的&#xff0c;其实也没必要。趁这个机会直接把…

[Java EE] 多线程(六):线程池与定时器

1. 线程池 1.1 什么是线程池 我们前面提到,线程的创建要比进程开销小,但是如果线程的创建/销毁比较频繁,开销也会比较大.所以我们便引入了线程池,线程池的作用就是提前把线程都创建好,放到用户态代码中写的数据结构中,后面就可以随用随取. 线程池最大的好处就是减少每次启动,…

【Canvas】给图片绘制矩形以及添加文字

效果图: <!DOCTYPE html> <html lang"en"><head><title>Canvas Marker Example</title></head><body><!-- 图片 --><imgid"myImage"src图片地址alt"Image to mark"style"display: no…

java-函数式编程-函数对象

定义 什么是合格的函数&#xff1f;无论多少次执行函数&#xff0c;只要输入一样&#xff0c;输出就不会改变 对象方法的简写 其实在类中&#xff0c;我们很多参数中都有一个this&#xff0c;被隐藏传入了 函数也可以作为对象传递&#xff0c;lambda就是很好的例子 函数式接口中…

uniapp0基础编写安卓原生插件和调用第三方jar包和编写语音播报插件之使用jar包插件

前言 如果你不会编写安卓插件,你可以先看看我之前零基础的文章(uniapp0基础编写安卓原生插件和调用第三方jar包和编写语音播报插件之零基础编写安卓插件), 我们使用第三方包,jar包编写安卓插件 开始 把依赖包,放到某个模块的/libs目录(myTestPlug/libs) 还要到build…

Source-Free Domain Adaptation for Semantic Segmentation

Batch Normalization Statistics (BNS)&#xff0c;dual attention module (DAM).dual attention distillation (DAD)&#xff0c;intra-domain patch-level self-supervision module (IPSM).ADV means adversarial 引用的文献较老&#xff0c;不建议复现

java面试(MySQL)

优化 如何定位慢查询 方案一&#xff1a;开源工具 调试工具&#xff1a;Arthas 运维工具&#xff1a;Prometheus,Skywalking 方案二&#xff1a;MySQL自带慢日志 慢查询日志记录了所有执行时间超过指定参数&#xff08;llong_query_time,单位&#xff1a;秒&#xff0c;默认十…

操作系统(2)——进程线程

目录 小程一言专栏链接: [link](http://t.csdnimg.cn/8MJA9)基础概念线程详解进程详解进程间通信调度常用调度算法 重要问题哲学家进餐问题问题的描述策略 读者-写者问题问题的描述两种情况策略 总结进程线程一句话 小程一言 本操作系统专栏&#xff0c;是小程在学操作系统的过…

专注 APT 攻击与防御—工具介绍Veil-Evasion

专注 APT 攻击与防御 - Micro8 系列教程项目地址&#xff1a;https://github.com/Veil-Framework/Veil-Evasion 1、Veil-Evasion Veil-Evasion 是与 Metasploit 生成相兼容的 Payload 的一款辅助框架&#xff0c;并可以绕过大多数的杀软。 Veil-Evasion 并没有集成在kali&am…

macOS sonoma 14.4.1编译JDK 12

macOS sonoma 14.4.1编译JDK 12 环境参考文档开始简述问题心路历程着手解决最终解决(前面有点啰嗦了&#xff0c;可以直接看这里) 记录一次靠自己看代码解决问题的经历(总之就是非常开心)。 首先&#xff0c;先diss一下bing&#xff0c;我差一点就放弃了。 环境 macOS sonom…

nginx--自定义日志跳转长连接文件缓存状态页

自定义日志服务 [rootlocalhost ~]# cat /apps/nginx/conf/conf.d/pc.conf server {listen 80;server_name www.fxq.com;error_log /data/nginx/logs/fxq-error.log info;access_log /data/nginx/logs/fxq-access.log main;location / {root /data/nginx/html/pc;index index…

Copilot Venture Studio創始合伙人楊林苑確認出席“邊緣智能2024 - AI開發者峰會”

隨著AI技術的迅猛發展&#xff0c;全球正逐步進入邊緣計算智能化與分布式AI深度融合的新時代&#xff0c;共同書寫著分布式智能創新應用的壯麗篇章。邊緣智能&#xff0c;作為融合邊緣計算和智能技術的新興領域&#xff0c;正逐漸成為推動AI發展的關鍵力量。借助分布式和去中心…

在Mac上恢复已删除文件夹的最佳方法

“嗨&#xff0c;我从我的Mac Documents文件夹中删除了很多文件夹。已删除的文件夹包含我的重要文档和文件&#xff0c;是否可以取回它们&#xff1f;垃圾桶已被清洁软件清空。如何在我的Mac上恢复已删除的文件夹&#xff1f; 当您在 Mac 上删除 1 或 2 个文件夹时&#xff0c…

字符串函数与字符函数运用(1)

字符串与字符函数介绍1 前言一、字符分类函数字符函数练习 二、字符函数转换1.引入库2.代码改进 字符串函数strlen函数strcpy 结尾 前言 字符串函数大概有以下这几种 strcpy、strcat 、strcmp、strncpy、strncat、strncmp、strstr、strtok、strerror 这些函数可以很好的解决你…

Java 笔记 12:Java 方法的相关使用,方法重载、参数传递,以及递归等内容

一、前言 记录时间 [2024-05-02] 系列文章简摘&#xff1a; Java 笔记 01&#xff1a;Java 概述&#xff0c;MarkDown 常用语法整理 Java 笔记 02&#xff1a;Java 开发环境的搭建&#xff0c;IDEA / Notepad / JDK 安装及环境配置&#xff0c;编写第一个 Java 程序 Java 笔记 …

队列以及信号量

什么是队列 队列又称消息队列&#xff0c;是一种常用于任务间通信的数据结构&#xff0c;队列可以在任务与任务间、中断和任 务间传递信息。 为什么不使用全局变量&#xff1f; 如果使用全局变量&#xff0c;兔子&#xff08;任务1&#xff09;修改了变量 a &#xff0c;等待树…

vulnhub靶场之FunBox-1

一.环境搭建 1.靶场描述 Boot2Root ! This is a reallife szenario, but easy going. You have to enumerate and understand the szenario to get the root-flag in round about 20min. This VM is created/tested with Virtualbox. Maybe it works with vmware. If you n…

81、动态规划-爬楼梯

思路: 爬楼梯是一个特别经典的动态规划题&#xff0c;动态规划最好的办法就是从递归改到动态规划。 比如现在n阶楼梯&#xff0c;每次爬1阶或者2阶&#xff0c;一共有多少种方法。那么我就可以全排列&#xff0c;比如当前我可以走一阶算一下有多少种方法&#xff0c;然后我可…

1.C#图像区域分割与提取

&#xff08;1&#xff09;创建一个名为SplitImage的窗体的应用程序&#xff0c;将窗体改名为FormSplitImage。 &#xff08;2&#xff09;创建一个名为ImageProcessingLibrary的类库程序&#xff0c;为该工程添加名为ImageProcessing的静态类 &#xff08;3&#xff09;为Imag…