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

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

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

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

运行环境 jdk17 andriod 34 compose material3

简介

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

在这里插入图片描述

UI界面

在这里插入图片描述

package com.example.myapplication.ui

import 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.File


var 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 Activity
    BottomAppBar(
        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>
....
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.example.myapplication.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android: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.fromFile
        if (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博客

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop3:集群搭建及常用命令与shell脚本整理(入门篇,从零开始搭建)

一、集群环境说明 1、用VMware安装3台Centos7.9虚拟机 2、虚拟机配置&#xff1a;2C&#xff0c;2G内存&#xff0c;50G存储 3、集群架构 从表格中&#xff0c;可以看出&#xff0c;Hadoop集群&#xff0c;主要有2部分&#xff0c;一个是HDFS服务&#xff0c;一个是YARN服务 …

[系统安全] 六十.威胁狩猎 (1)APT攻击检测及防御与常见APT组织的攻击案例分析

您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列。因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全、逆向分析和恶意代码检测,“系统安全”系列文章会更加聚焦,更加系…

四、管道与重定向

四、管道与重定向 1 重定向 0,标准输入(键盘) 1,标准输出 2,标准错误&#xff0c; 3,进程在执行过程中打开的其他文件。 &:表示正确错误混合输出1.1 输出重定向 (覆盖&#xff0c;追加) > ----覆盖 >> ----追加 正确输出&#xff1a; 1> 1>> 等价…

Git 仓库内容操作

Git 仓库内容操作 | CoderMast编程桅杆Git 仓库内容操作 添加文件到暂存区 使用如下指令将工作区的文件添加到暂存区&#xff0c;告诉 Git 在下次 commit 时哪些文件做出了修改。 commit 指令详看后续 添加一个或多个文件到暂存区&#xff1a; 添加指定目录到暂存区 添加当前目…

ffmpeg与sdl的个人笔记

说明 这里的ffmpeg基础知识和sdl基础知识仅提及与示例代码相关的知识点, 进阶可学习雷神的博客。 https://blog.csdn.net/leixiaohua1020 当然&#xff0c;如代码写的有问题或有更好的见解&#xff0c;欢迎指正&#xff01; 音视频基础知识 在学习音视频理论知识时&#xff…

CSS中设置透明度的2个属性:opacity,RGBA以及它们的区别

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

试用了三个Ai音乐工具,我的偶像河图要完蛋了

试了三个生成音乐的ai工具&#xff0c;分别是爆火的suno,后期新秀udio&#xff0c;还有我们国内的天工。 先说感受&#xff0c;suno和天工我觉得稍微靠前&#xff0c;udio可能我的配置风格有问题&#xff0c;啪啪啪连选了好几个风格&#xff0c;生成的东西有点怪。 我随手写了…

【机器学习基础1】什么是机器学习、预测模型解决问题的步骤、机器学习的Python生态圈

文章目录 一. 什么是机器学习1. 概念2. 机器学习算法分类 二. 利用预测模型解决问题的步骤三. 机器学习的Python生态圈 一. 什么是机器学习 1. 概念 机器学习&#xff08;Machine Learning&#xff0c;ML&#xff09;是一门多领域的交叉学科&#xff0c;涉及概率论、统计学、…

深度学习 --- stanford cs231学习笔记(一)

stanford cs231学习笔记(一) 1&#xff0c;先是讲到了机器学习中的kNN算法&#xff0c;然后因为kNN分类器的一些弊端&#xff0c;引入了线性分类器。 kNN算法的三大弊端&#xff1a; (1)&#xff0c;计算量大&#xff0c;当特征比较多时表示性差 (2)&#xff0c;训练时耗时少…

01 - 安装Kettle

下载安装包 我这边提供的安装包是绿色版的&#xff0c;开箱即用 Kettle.exe 阿里云盘分享 提取码: 8sd5 点击链接保存&#xff0c;或者复制本段内容&#xff0c;打开「阿里云盘」APP &#xff0c;无需下载极速在线查看&#xff0c;视频原画倍速播放。 启动步骤 解压 双击Spo…

注意力机制(四)(多头注意力机制)

​&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;《深度学习基础知识》 相关专栏&#xff1a; ⚽《机器学习基础知识》 &#x1f3d0;《机器学习项目实战》 &#x1f94e;《深度学习项目实…

Python | Leetcode Python题解之第55题跳跃游戏

题目&#xff1a; 题解&#xff1a; class Solution:def canJump(self, nums: List[int]) -> bool:n, rightmost len(nums), 0for i in range(n):if i < rightmost:rightmost max(rightmost, i nums[i])if rightmost > n - 1:return Truereturn False

闲话 Asp.Net Core 数据校验(三)EF Core 集成 FluentValidation 校验数据例子

前言 一个在实际应用中 EF Core 集成 FluentValidation 进行数据校验的例子。 Step By Step 步骤 创建一个 Asp.Net Core WebApi 项目 引用以下 Nuget 包 FluentValidation.AspNetCore Microsoft.AspNetCore.Identity.EntityFrameworkCore Microsoft.EntityFrameworkCore.Re…

Unity 合并子物体获得简化Mesh

合并子物体获得简化Mesh &#x1f959;环境&#x1f96a;Demo &#x1f959;环境 PackageManager安装Editor Coroutines 导入插件&#x1f448; &#x1f96a;Demo 生成参数微调&#xff1a;Assets/EasyColliderEditor/Scripts/VHACDSettings/VHACDSettings.asset

TDengine高可用架构之TDengine+Keepalived

之前在《TDengine高可用探讨》提到过&#xff0c;TDengine通过多副本和多节点能够保证数据库集群的高可用。单对于应用端来说&#xff0c;如果使用原生连接方式&#xff08;taosc&#xff09;还好&#xff0c;当一个节点下线&#xff0c;应用不会受到影响&#xff1b;但如果使用…

Kafka 3.x.x 入门到精通(03)——Kafka基础生产消息

Kafka 3.x.x 入门到精通&#xff08;03&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.4.1 生产消息的基本步骤2.4.2 生产消息的基本代码2.4.3 发送消息2.4.3.1 拦截器2.4.3.1.1 增加拦截器类2.4.3.1.2 配置拦截器 2.4.3…

Mysql事务—隔离级别—脏读、不可重复读、幻读-遥遥领先版

事务的基本概念 事务就是一组原子性的操作&#xff0c;这些操作要么全部发生&#xff0c;要么全部不发生。事务把数据库从一种一致性状态转换成另一种一致性状态。 事务最经典也经常被拿出来说例子就是转账了。 假如小明要给小红转账1000元&#xff0c;这个转账会涉及到两个…

Linux进程——进程的概念(PCB的理解)

前言&#xff1a;在了解完冯诺依曼体系结构和操作系统之后&#xff0c;我们进入了Linux的下一篇章Linux进程&#xff0c;但在学习Linux进程之前&#xff0c;一定要阅读理解上一篇内容&#xff0c;理解“先描述&#xff0c;再组织”才能更好的理解进程的含义。 Linux进程学习基…

【中级软件设计师】上午题12-软件工程(3):项目活动图、软件风险、软件评审、软件项目估算

【中级软件设计师】上午题12-软件工程&#xff08;3&#xff09; 1 软件项目估算1.1 COCOMO估算模型1.2 COCOMOⅡ模型 2 进度管理2.1 gantt甘特图2.2 pert图2.3 项目活动图2.3.1 画项目图 3 软件配置管理4 软件风险4.1 风险管理4.2 风险识别4.3 风险预测4.4 风险评估4.5 风险控…

二叉树遍历递归法迭代法实现

一.递归法实现二叉树遍历 前序遍历 创建一个节点类 属性是val,左节点&#xff0c;右节点 public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val x; } } 前序遍历 class Solution {public List<Integer> preorderTraversa…