添加Json 数据文件接收功能。
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Thu Nov 13 14:05:41 HKT 2025
|
||||
#Thu Nov 13 08:15:14 GMT 2025
|
||||
stageCount=1
|
||||
libraryProject=
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.0
|
||||
buildCount=0
|
||||
buildCount=9
|
||||
baseBetaVersion=15.11.1
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<!-- 仅保留Android 8.0以下所需权限,若最低版本≥API26可移除 -->
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.location.gps"
|
||||
@@ -28,7 +30,24 @@
|
||||
<!-- 主Activity(核心页面,非启动入口) -->
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name">
|
||||
android:label="@string/app_name"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
|
||||
<action android:name="android.intent.action.EDIT"/>
|
||||
|
||||
<data android:mimeType="application/json"/>
|
||||
|
||||
<data android:mimeType="text/x-json"/>
|
||||
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- 独立CrashActivity(替换内部类声明,避免实例化失败) -->
|
||||
@@ -104,7 +123,18 @@
|
||||
<action android:name="cc.winboll.studio.positions.receivers.MotionStatusReceiver"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
|
||||
<!-- 声明文件提供者(如需对外分享保存的文件,可选) -->
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
@@ -20,9 +21,10 @@ import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.positions.activities.LocationActivity;
|
||||
import cc.winboll.studio.positions.activities.WinBoLLActivity;
|
||||
import cc.winboll.studio.positions.utils.AppConfigsUtil;
|
||||
import cc.winboll.studio.positions.utils.ServiceUtil;
|
||||
import cc.winboll.studio.positions.utils.APPPlusUtils;
|
||||
import cc.winboll.studio.positions.utils.AppConfigsUtil;
|
||||
import cc.winboll.studio.positions.utils.JsonShareHandler;
|
||||
import cc.winboll.studio.positions.utils.ServiceUtil;
|
||||
|
||||
/**
|
||||
* 主页面:仅负责
|
||||
@@ -89,6 +91,8 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
setContentView(R.layout.activity_main); // 关联主页面布局
|
||||
// 处理应用级别的切换请求
|
||||
handleSwitchRequest();
|
||||
// 处理启动时的分享 Intent
|
||||
handleShareIntent(getIntent());
|
||||
|
||||
// 设置当前应用级别
|
||||
App.setAppLevel(this);
|
||||
@@ -122,6 +126,30 @@ public class MainActivity extends WinBoLLActivity implements IWinBoLLActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
// 处理后续接收的分享 Intent(如应用已在后台)
|
||||
handleShareIntent(intent);
|
||||
}
|
||||
|
||||
private void handleShareIntent(Intent intent) {
|
||||
if (intent != null && Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||
// 调用工具类,弹出确认对话框
|
||||
JsonShareHandler.handleSharedJsonWithConfirm(this, intent, new JsonShareHandler.ConfirmCallback() {
|
||||
@Override
|
||||
public void onConfirm(boolean isConfirm) {
|
||||
// 回调处理:isConfirm 为 true 表示接收并保存,false 表示取消
|
||||
if (!isConfirm) {
|
||||
Log.d("MainActivity", "用户取消接收文件");
|
||||
// 可添加取消后的逻辑(如关闭页面)
|
||||
// finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package cc.winboll.studio.positions;
|
||||
import android.os.Bundle;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/13 15:21
|
||||
* @Describe MainActivityLaojun
|
||||
*/
|
||||
public class MainActivityLaojun extends MainActivity {
|
||||
|
||||
public static final String TAG = "MainActivityLaojun";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
ToastUtils.show("道法自然");
|
||||
LogUtils.d(TAG, "玩法归臻");
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package cc.winboll.studio.positions.utils;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/13 15:42
|
||||
* @Describe JsonShareHandler
|
||||
* 外部 JSON 文件分享处理工具类
|
||||
* 功能:接收外部分享的 .json 文件,弹出确认对话框,保存到外部存储 files/BaseBean 目录
|
||||
*/
|
||||
public class JsonShareHandler {
|
||||
private static final String TAG = "JsonShareHandler";
|
||||
private static final String TARGET_DIR = "BaseBean";
|
||||
private static final String MIME_TYPE_JSON = "application/json";
|
||||
private static final String FILE_SUFFIX_JSON = ".json";
|
||||
// 对话框回调接口(Java7 无 Lambda,用接口实现)
|
||||
public interface ConfirmCallback {
|
||||
void onConfirm(boolean isConfirm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理外部分享的 Intent,先弹出确认对话框,再决定是否接收文件
|
||||
* @param context 上下文(需为 Activity,否则无法弹出对话框)
|
||||
* @param intent 分享 Intent
|
||||
* @param callback 确认结果回调(用于 Activity 处理后续逻辑)
|
||||
*/
|
||||
public static void handleSharedJsonWithConfirm(final Context context, final Intent intent, final ConfirmCallback callback) {
|
||||
if (context == null || intent == null || callback == null) {
|
||||
Log.e(TAG, "参数为空,处理失败");
|
||||
if (callback != null) callback.onConfirm(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 先验证 Intent 合法性(提前过滤无效分享)
|
||||
String action = intent.getAction();
|
||||
String type = intent.getType();
|
||||
if (!Intent.ACTION_SEND.equals(action) || type == null) {
|
||||
Log.e(TAG, "非文件分享 Intent");
|
||||
Toast.makeText(context, "不支持的分享类型", Toast.LENGTH_SHORT).show();
|
||||
callback.onConfirm(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 弹出确认对话框
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle("接收 JSON 文件")
|
||||
.setMessage("是否接收并保存该 JSON 文件?")
|
||||
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
// 3. 点击 Yes,处理文件保存
|
||||
String savedPath = handleSharedJsonFile(context, intent);
|
||||
if (savedPath != null) {
|
||||
Toast.makeText(context, "文件保存成功:" + savedPath, Toast.LENGTH_LONG).show();
|
||||
callback.onConfirm(true);
|
||||
} else {
|
||||
Toast.makeText(context, "文件保存失败", Toast.LENGTH_SHORT).show();
|
||||
callback.onConfirm(false);
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
// 4. 点击 No,直接退出处理
|
||||
callback.onConfirm(false);
|
||||
}
|
||||
})
|
||||
.setCancelable(false) // 不可点击外部取消
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心文件处理逻辑(原有功能,无修改)
|
||||
*/
|
||||
private static String handleSharedJsonFile(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
String type = intent.getType();
|
||||
|
||||
// 验证 JSON 格式
|
||||
if (!MIME_TYPE_JSON.equals(type) && !type.contains("json")) {
|
||||
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
if (uri == null || !getFileNameFromUri(context, uri).endsWith(FILE_SUFFIX_JSON)) {
|
||||
Log.e(TAG, "接收的文件不是 JSON 格式");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Uri sharedUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
if (sharedUri == null) {
|
||||
Log.e(TAG, "未获取到分享的文件 Uri");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建保存目录
|
||||
File saveDir = getTargetSaveDir(context);
|
||||
if (!saveDir.exists() && !saveDir.mkdirs()) {
|
||||
Log.e(TAG, "创建保存目录失败:" + saveDir.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 解析文件名(兼容低版本)
|
||||
String fileName = getFileNameFromUri(context, sharedUri);
|
||||
if (fileName == null || !fileName.endsWith(FILE_SUFFIX_JSON)) {
|
||||
fileName = "default_" + System.currentTimeMillis() + FILE_SUFFIX_JSON;
|
||||
Log.w(TAG, "文件名解析失败,使用默认名称:" + fileName);
|
||||
}
|
||||
|
||||
// 复制文件
|
||||
File targetFile = new File(saveDir, fileName);
|
||||
boolean copySuccess = copyFileFromUri(context, sharedUri, targetFile);
|
||||
return copySuccess ? targetFile.getAbsolutePath() : null;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "处理分享文件异常:" + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标保存目录(兼容 Android 10+ 分区存储)
|
||||
*/
|
||||
private static File getTargetSaveDir(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
return new File(context.getExternalFilesDir(null), TARGET_DIR);
|
||||
} else {
|
||||
return new File(
|
||||
Environment.getExternalStorageDirectory() + File.separator +
|
||||
"Android" + File.separator +
|
||||
"data" + File.separator +
|
||||
context.getPackageName() + File.separator +
|
||||
"files" + File.separator +
|
||||
TARGET_DIR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Uri 解析文件名(兼容所有 Android 版本)
|
||||
*/
|
||||
private static String getFileNameFromUri(Context context, Uri uri) {
|
||||
if (uri == null) return null;
|
||||
|
||||
// 1. 文件 Uri(file:// 开头)
|
||||
if ("file".equals(uri.getScheme())) {
|
||||
return new File(uri.getPath()).getName();
|
||||
}
|
||||
|
||||
// 2. 内容 Uri(content:// 开头)
|
||||
if ("content".equals(uri.getScheme())) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = context.getContentResolver().query(
|
||||
uri,
|
||||
new String[]{MediaStore.MediaColumns.DISPLAY_NAME},
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
if (nameIndex != -1) {
|
||||
return cursor.getString(nameIndex);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "解析内容 Uri 文件名失败:" + e.getMessage());
|
||||
} finally {
|
||||
if (cursor != null) cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 解析失败,返回默认名称
|
||||
String lastPathSegment = uri.getLastPathSegment();
|
||||
return lastPathSegment != null ? lastPathSegment : "unknown.json";
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制 Uri 指向的文件到目标路径
|
||||
*/
|
||||
private static boolean copyFileFromUri(Context context, Uri sourceUri, File targetFile) {
|
||||
InputStream inputStream = null;
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
inputStream = context.getContentResolver().openInputStream(sourceUri);
|
||||
if (inputStream == null) {
|
||||
Log.e(TAG, "无法打开源文件输入流");
|
||||
return false;
|
||||
}
|
||||
|
||||
outputStream = new FileOutputStream(targetFile);
|
||||
byte[] buffer = new byte[1024 * 4];
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
outputStream.flush();
|
||||
Log.d(TAG, "文件保存成功:" + targetFile.getAbsolutePath());
|
||||
return true;
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "文件复制异常:" + e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null) inputStream.close();
|
||||
if (outputStream != null) outputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "关闭流异常:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查外部存储是否可用
|
||||
*/
|
||||
public static boolean isExternalStorageAvailable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
}
|
||||
|
||||
6
positions/src/main/res/xml/file_paths.xml
Normal file
6
positions/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<external-files-path
|
||||
name="BaseBean"
|
||||
path="BaseBean/" />
|
||||
</paths>
|
||||
Reference in New Issue
Block a user