添加Json 数据文件接收功能。
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#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
|
stageCount=1
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.11
|
baseVersion=15.11
|
||||||
publishVersion=15.11.0
|
publishVersion=15.11.0
|
||||||
buildCount=0
|
buildCount=9
|
||||||
baseBetaVersion=15.11.1
|
baseBetaVersion=15.11.1
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<!-- 仅保留Android 8.0以下所需权限,若最低版本≥API26可移除 -->
|
<!-- 仅保留Android 8.0以下所需权限,若最低版本≥API26可移除 -->
|
||||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
<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
|
<uses-feature
|
||||||
android:name="android.hardware.location.gps"
|
android:name="android.hardware.location.gps"
|
||||||
@@ -28,7 +30,24 @@
|
|||||||
<!-- 主Activity(核心页面,非启动入口) -->
|
<!-- 主Activity(核心页面,非启动入口) -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
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>
|
</activity>
|
||||||
|
|
||||||
<!-- 独立CrashActivity(替换内部类声明,避免实例化失败) -->
|
<!-- 独立CrashActivity(替换内部类声明,避免实例化失败) -->
|
||||||
@@ -104,7 +123,18 @@
|
|||||||
<action android:name="cc.winboll.studio.positions.receivers.MotionStatusReceiver"/>
|
<action android:name="cc.winboll.studio.positions.receivers.MotionStatusReceiver"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
@@ -20,9 +21,10 @@ import cc.winboll.studio.libappbase.LogUtils;
|
|||||||
import cc.winboll.studio.libappbase.ToastUtils;
|
import cc.winboll.studio.libappbase.ToastUtils;
|
||||||
import cc.winboll.studio.positions.activities.LocationActivity;
|
import cc.winboll.studio.positions.activities.LocationActivity;
|
||||||
import cc.winboll.studio.positions.activities.WinBoLLActivity;
|
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.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); // 关联主页面布局
|
setContentView(R.layout.activity_main); // 关联主页面布局
|
||||||
// 处理应用级别的切换请求
|
// 处理应用级别的切换请求
|
||||||
handleSwitchRequest();
|
handleSwitchRequest();
|
||||||
|
// 处理启动时的分享 Intent
|
||||||
|
handleShareIntent(getIntent());
|
||||||
|
|
||||||
// 设置当前应用级别
|
// 设置当前应用级别
|
||||||
App.setAppLevel(this);
|
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
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.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