diff --git a/positions/build.properties b/positions/build.properties index 4c8791f3..251798ae 100644 --- a/positions/build.properties +++ b/positions/build.properties @@ -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 diff --git a/positions/src/main/AndroidManifest.xml b/positions/src/main/AndroidManifest.xml index f9b4719d..1c69bdb8 100644 --- a/positions/src/main/AndroidManifest.xml +++ b/positions/src/main/AndroidManifest.xml @@ -12,6 +12,8 @@ + + + android:label="@string/app_name" + android:exported="true"> + + + + + + + + + + + + + + + + @@ -104,7 +123,18 @@ - + + + + + + diff --git a/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java index f9ef47f0..e1deb539 100644 --- a/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java +++ b/positions/src/main/java/cc/winboll/studio/positions/MainActivity.java @@ -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(); diff --git a/positions/src/main/java/cc/winboll/studio/positions/MainActivityLaojun.java b/positions/src/main/java/cc/winboll/studio/positions/MainActivityLaojun.java new file mode 100644 index 00000000..15b5da56 --- /dev/null +++ b/positions/src/main/java/cc/winboll/studio/positions/MainActivityLaojun.java @@ -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&豆包大模型 + * @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); + } + +} diff --git a/positions/src/main/java/cc/winboll/studio/positions/utils/JsonShareHandler.java b/positions/src/main/java/cc/winboll/studio/positions/utils/JsonShareHandler.java new file mode 100644 index 00000000..13095c6e --- /dev/null +++ b/positions/src/main/java/cc/winboll/studio/positions/utils/JsonShareHandler.java @@ -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&豆包大模型 + * @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); + } +} + diff --git a/positions/src/main/res/xml/file_paths.xml b/positions/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..802e4cc6 --- /dev/null +++ b/positions/src/main/res/xml/file_paths.xml @@ -0,0 +1,6 @@ + + + +