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

news2024/10/6 1:35:21

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博客

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

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

相关文章

JAVA面试专题-Redis

你在最近的项目中哪些场景使用了Redis 缓存 缓存穿透 缓存穿透&#xff1a;查询一个不存在的数据&#xff0c;mysql查询不到数据也不好直接写入缓存&#xff0c;导致每次请求都查数据库。 解决方案一&#xff1a;缓存空数据&#xff0c;即使查询返回的数据为空&#xff0c;也把…

xss漏洞学习

1.xss漏洞简介 跨站脚本&#xff08;Cross-Site Scripting&#xff09;&#xff0c;本应该缩写为CSS&#xff0c;但是该缩写已被层叠样式脚本Cascading Style Sheets所用&#xff0c;所以改简称为XSS。也称跨站脚本或跨站脚本攻击。 原理&#xff1a;跨站脚本攻击XSS通过将恶…

SQLite如何处理CSV 虚拟表(三十七)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite的DBSTAT 虚拟表&#xff08;三十六&#xff09; 下一篇:SQLite的扩展函数Carray()表值函数(三十八) ​ RFC4180格式是一种文本文件格式&#xff0c;被用于表格数据间的交互&#xff0c;也可将表格数据转化…

Java进阶-JINQ详解与使用

本文详细介绍了JINQ&#xff08;Java Integrated Query&#xff09;&#xff0c;一种强化Java中数据查询能力的库&#xff0c;提供类SQL的查询语法和类型安全的操作。文章首先解释了JINQ的基本功能和应用&#xff0c;随后通过具体示例展示了如何使用JINQ进行数据过滤、投影、连…

Python自学篇3-PyCharm开发工具下载、安装及应用

一、Python开发工具 自学篇1中讲到了安装Python之后出现的几个应用程序&#xff0c;其中IDLE、Python.exe都可以用来编写python程序&#xff0c;也可以进行调试&#xff1b;但是比较基础&#xff0c;比较原始&#xff0c;调试不方便&#xff0c;界面也不友好&#xff0c;需要更…

天空卫士旗舰产品入选《网络安全专用产品指南》

权威认证 近日&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;发布了第一版《网络安全专用产品指南》。这一权威指南中&#xff0c;天空卫士荣获殊荣&#xff0c;旗下三款尖端产品荣耀入选&#xff0c;分别是增强型Web安全网关&#xff08;ASWG&#xff09;、数…

解析Redis Key Prefix配置之谜:双冒号“::”的由来与作用

前言 在使用Spring Boot集成Redis进行应用开发时&#xff0c;为了增强缓存键的可读性和管理性&#xff0c;我们常常会在配置文件中设定一个全局的key-prefix。如果你发现存储至Redis的键自动附加了“::”&#xff0c;本文将深入探讨这一现象背后的原因&#xff0c;解析Spring …

长难句打卡4.29

If appropriate public policies were in place to help all women—whether CEOs or their children’s caregivers—and all families, Sandberg would be no more newsworthy than any other highly capable person living in a more just society 如果能制定适当的公共政策…

解决Pycharm全局搜索与输入法简繁切换快捷键冲突问题

Pycharm中全局搜索快捷键Ctrl Shift F 如图所示&#xff1a; 微软输入法简繁切换快捷键设置&#xff1a; 解决办法&#xff1a; 关掉输入法的切换功能即可&#xff0c;或者更改简繁切换快捷键&#xff0c;毕竟简繁切换使用频率极低。

智慧农业设备——虫情监测系统

随着科技的不断进步和农业生产的日益现代化&#xff0c;智慧农业成为了新时代农业发展的重要方向。其中&#xff0c;虫情监测系统作为智慧农业的重要组成部分&#xff0c;正逐渐受到广大农户和农业专家的关注。 虫情监测系统是一种基于现代传感技术、图像识别技术和大数据分析技…

Django-admin组件

Django-admin组件 admin是django中提供的一套可视化工具&#xff1a;用于对ORM中定义的表进行增删改查。 1 概览 在django项目启动时&#xff0c;自动找到注册到admin中的所有model中定义的类&#xff0c;然后为这些类生成一系列的URL和视图函数&#xff0c;实现基本增删改查…

202009青少年软件编程(Python)等级考试试卷(一级)

第 1 题 【单选题】 Python自带的编程环境是&#xff1f;&#xff08; &#xff09; A :PyScripter B :Spyder C :Notepad D :IDLE 正确答案:D 试题解析: 第 2 题 【单选题】 假设a2,b3,那么a-b*b的值是&#xff1f;( ) A :-3 B :-2 C :-7 D :-11 正确答案:C 试题…

基于ESP32—CAM物联网WIFI小车

一.功能概述 摄像头的画面可以实时的传输到&#xff0c;点灯科技APP的手机端&#xff0c;这样可以实时查看周围环境的状况&#xff0c;灯光不足&#xff0c;画面不清晰时可以打开灯光照明。手机端有左转、右转、前进、后退、停止的按钮。可以根据自己需要&#xff0c;来控制小车…

变革 Perplexica:AI驱动的问答搜索引擎

Perplexica是一个开源的人工智能搜索工具&#xff0c;也可以说是一款人工智能搜索引擎&#xff0c;它深入互联网以找到答案。受Perplexity AI启发&#xff0c;它是一个开源选择&#xff0c;不仅可以搜索网络&#xff0c;还能理解您的问题。它使用先进的机器学习算法&#xff0c…

【doghead】修改abseil-cpp 构造worker成功

台式机 13900k 的wsl2 ubuntu22.04 环境root@DESKTOP-1723CM1:/mnt/d/XTRANS/thunderbolt/ayame/zhb-bifrost/Bifrost-202403/worker/build# uname -a Linux DESKTOP-1723CM1 5.15.146.1-microsoft-standard-WSL2 #1 SMP Thu Jan 11 04:09:03 UTC 2024 x86_64 x86_64 x86_64 GN…

电商技术揭秘四十:电商智能风控系统决策引擎浅析

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘二十八&#xff1a;安全与合规性保障 电商技术揭秘二十九&#xff1a;电商法律合规浅析 电商技术揭秘三十&#xff1a;知识产权保…

Grafana页面嵌入自建Web应用页面

目录 一、应用场景 二、实现方式 1、修改Grafana配置文件 2、获取监控页面url 3、隐藏左侧和顶部菜单 一、应用场景 需要将Grafana监控页面嵌入自建Web应用页面&#xff0c;使Grafana监控页面成为自建Web应用的一部分。 二、实现方式 总体思路&#xff1a;修改Grafana配…

嵌入式移植7Z解压缩(纯C)

本文分享一个纯C语言编写的7Z解压缩代码库&#xff0c;本代码库的主要目的是在嵌入式环境下使用7z解压缩文件&#xff0c;可以将升级包通过7z进行压缩&#xff0c;然后发送给设备&#xff0c;减小和设备传输过程中的文件大小&#xff0c;进而达到传输大文件的目的。 下载链接 …

[CUDA 学习笔记] GEMM 优化: 双缓冲 (Prefetch) 和 Bank Conflict 解决

GEMM 优化: 双缓冲 (Prefetch) 和 Bank Conflict 解决 前言 本文主要是对 深入浅出GPU优化系列&#xff1a;GEMM优化&#xff08;一&#xff09; - 知乎, 深入浅出GPU优化系列&#xff1a;GEMM优化&#xff08;二&#xff09; - 知乎 以及 深入浅出GPU优化系列&#xff1a;GE…

Python数组类+AI插件

目录 规划实现初始化插入删除查找 AI插件单测注释调优建议 小结 规划 先想清楚都写哪些&#xff0c;然后再动手操作 用Python写了一个简单数组类&#xff0c;首先思考下都写哪些功能&#xff1a; 插入删除查找用插件做单元测试和写注释 目的只是实现一个简单的数组类&#x…