migrate
This commit is contained in:
parent
12e74d202c
commit
442315a743
|
@ -8,6 +8,7 @@ import android.os.Environment
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
import android.util.Log
|
||||||
import android.view.Display
|
import android.view.Display
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.annotation.NonNull
|
import androidx.annotation.NonNull
|
||||||
|
@ -24,6 +25,10 @@ import mobile.Mobile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
|
import java.lang.IllegalStateException
|
||||||
|
import java.nio.file.CopyOption
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.StandardCopyOption
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
class MainActivity : FlutterActivity() {
|
class MainActivity : FlutterActivity() {
|
||||||
|
@ -54,6 +59,7 @@ class MainActivity : FlutterActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Log.e("Method", "Exception", e)
|
||||||
uiThreadHandler.post {
|
uiThreadHandler.post {
|
||||||
error("", e.message, "")
|
error("", e.message, "")
|
||||||
}
|
}
|
||||||
|
@ -90,11 +96,11 @@ class MainActivity : FlutterActivity() {
|
||||||
// exportComicDownloadAndroidQ(call.argument("comicId")!!)
|
// exportComicDownloadAndroidQ(call.argument("comicId")!!)
|
||||||
// }
|
// }
|
||||||
// 现在的文件储存路径, 默认路径返回空字符串 ""
|
// 现在的文件储存路径, 默认路径返回空字符串 ""
|
||||||
"androidDataLocal" -> androidDataLocal()
|
"dataLocal" -> androidDataLocal()
|
||||||
|
// 迁移到那个地方, 如果是空字符串则迁移会默认位置
|
||||||
|
"migrate" -> androidMigrate(call.argument("path")!!)
|
||||||
// 获取可以迁移数据地址
|
// 获取可以迁移数据地址
|
||||||
"androidGetExtendDirs" -> androidGetExtendDirs()
|
"androidGetExtendDirs" -> androidGetExtendDirs()
|
||||||
// 迁移到那个地方, 如果是空字符串则迁移会默认位置
|
|
||||||
"androidMigrate" -> androidMigrate(call.argument("path")!!)
|
|
||||||
else -> {
|
else -> {
|
||||||
notImplementedToken
|
notImplementedToken
|
||||||
}
|
}
|
||||||
|
@ -159,8 +165,12 @@ class MainActivity : FlutterActivity() {
|
||||||
|
|
||||||
private fun androidGetExtendDirs(): String {
|
private fun androidGetExtendDirs(): String {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
return context!!.getExternalFilesDirs("")?.joinToString(",") { it.toString() }
|
val result = context!!.getExternalFilesDirs("")?.toMutableList()?.also {
|
||||||
?: ""
|
it.add(context!!.filesDir.absoluteFile)
|
||||||
|
}?.joinToString("|")
|
||||||
|
if (result != null) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw Exception("System version too low")
|
throw Exception("System version too low")
|
||||||
}
|
}
|
||||||
|
@ -170,13 +180,21 @@ class MainActivity : FlutterActivity() {
|
||||||
if (current == path) {
|
if (current == path) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Runtime.getRuntime().exec(
|
// 删除位置配置文件
|
||||||
arrayOf(
|
if (File(current, "data.local").exists()) {
|
||||||
"mv",
|
File(current, "data.local").delete()
|
||||||
"$current/*",
|
}
|
||||||
"$path/"
|
// 目标位置文件夹不存在就创建,存在则清理
|
||||||
)
|
val target = File(path)
|
||||||
).exitValue()
|
if (!target.exists()) {
|
||||||
|
target.mkdirs()
|
||||||
|
}
|
||||||
|
target.listFiles().forEach { delete(it) }
|
||||||
|
// 移动所有文件夹
|
||||||
|
|
||||||
|
File(current).listFiles().forEach {
|
||||||
|
move(it, File(target, it.name))
|
||||||
|
}
|
||||||
val localFile = File(context!!.filesDir.absolutePath, "data.local")
|
val localFile = File(context!!.filesDir.absolutePath, "data.local")
|
||||||
if (path == context!!.filesDir.absolutePath) {
|
if (path == context!!.filesDir.absolutePath) {
|
||||||
localFile.delete()
|
localFile.delete()
|
||||||
|
@ -185,6 +203,35 @@ class MainActivity : FlutterActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun delete(f: File) {
|
||||||
|
f.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun move(f: File, t: File) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
if (f.isDirectory) {
|
||||||
|
Files.createDirectories(t.toPath())
|
||||||
|
f.listFiles().forEach { move(it, File(t, it.name)) }
|
||||||
|
Files.delete(f.toPath())
|
||||||
|
} else {
|
||||||
|
Files.move(f.toPath(), t.toPath())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (f.isDirectory) {
|
||||||
|
t.mkdirs()
|
||||||
|
f.listFiles().forEach { move(it, File(t, it.name)) }
|
||||||
|
f.delete()
|
||||||
|
} else {
|
||||||
|
FileOutputStream(t).use { o ->
|
||||||
|
FileInputStream(f).use { i ->
|
||||||
|
o.write(i.readBytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// save_image
|
// save_image
|
||||||
|
|
||||||
private fun saveImage(path: String) {
|
private fun saveImage(path: String) {
|
||||||
|
@ -261,7 +308,7 @@ class MainActivity : FlutterActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume_buttons
|
// volume_buttons
|
||||||
|
|
||||||
private var volumeEvents: EventChannel.EventSink? = null
|
private var volumeEvents: EventChannel.EventSink? = null
|
||||||
|
|
||||||
|
@ -294,7 +341,7 @@ class MainActivity : FlutterActivity() {
|
||||||
return super.onKeyDown(keyCode, event)
|
return super.onKeyDown(keyCode, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安卓11以上使用了 MANAGE_EXTERNAL_STORAGE 权限来管理整个外置存储 (危险权限)
|
// 安卓11以上使用了 MANAGE_EXTERNAL_STORAGE 权限来管理整个外置存储 (危险权限)
|
||||||
// private val resourceQueue: LinkedBlockingQueue<Any?> = LinkedBlockingQueue()
|
// private val resourceQueue: LinkedBlockingQueue<Any?> = LinkedBlockingQueue()
|
||||||
// private var tmpComicId: String? = null
|
// private var tmpComicId: String? = null
|
||||||
// private val exportComicDownloadAndroidQRequestCode = 2
|
// private val exportComicDownloadAndroidQRequestCode = 2
|
||||||
|
|
|
@ -555,9 +555,10 @@ func FlatInvoke(method string, params string) (string, error) {
|
||||||
case "setDownloadRunning":
|
case "setDownloadRunning":
|
||||||
b, e := strconv.ParseBool(params)
|
b, e := strconv.ParseBool(params)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
setDownloadRunning(b)
|
|
||||||
}
|
|
||||||
return "", e
|
return "", e
|
||||||
|
}
|
||||||
|
setDownloadRunning(b)
|
||||||
|
return "", nil
|
||||||
case "createDownload":
|
case "createDownload":
|
||||||
return "", createDownload(params)
|
return "", createDownload(params)
|
||||||
case "addDownload":
|
case "addDownload":
|
||||||
|
|
|
@ -537,4 +537,20 @@ class Method {
|
||||||
Future<int> androidGetVersion() async {
|
Future<int> androidGetVersion() async {
|
||||||
return await _channel.invokeMethod("androidGetVersion", {});
|
return await _channel.invokeMethod("androidGetVersion", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> dataLocal() async {
|
||||||
|
return await _channel.invokeMethod("dataLocal", {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<String>> androidGetExtendDirs() async {
|
||||||
|
String? tmp = await _channel.invokeMethod("androidGetExtendDirs", {});
|
||||||
|
if (tmp != null && tmp.isNotEmpty) {
|
||||||
|
return tmp.split("|");
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future migrate(String path) async {
|
||||||
|
return _channel.invokeMethod("migrate", {"path": path});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,6 @@ class _DownloadListScreenState extends State<DownloadListScreen> {
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
var to = !_downloadRunning;
|
var to = !_downloadRunning;
|
||||||
// properties.saveDownloading(to);
|
|
||||||
await method.setDownloadRunning(to);
|
await method.setDownloadRunning(to);
|
||||||
setState(() {
|
setState(() {
|
||||||
_downloadRunning = to;
|
_downloadRunning = to;
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pikapi/basic/Common.dart';
|
||||||
|
import 'package:pikapi/basic/Method.dart';
|
||||||
|
import 'package:pikapi/screens/components/ContentBuilder.dart';
|
||||||
|
import 'package:pikapi/screens/components/ContentLoading.dart';
|
||||||
|
|
||||||
|
class MigrateScreen extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _MigrateScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MigrateScreenState extends State<MigrateScreen> {
|
||||||
|
late Future _future = _load();
|
||||||
|
late String _current;
|
||||||
|
late List<String> paths;
|
||||||
|
String _message = "";
|
||||||
|
|
||||||
|
int _migrate = 0; // 0 没有开始迁移,1 正在迁移,2 迁移成功,3 迁移失败
|
||||||
|
|
||||||
|
Future _load() async {
|
||||||
|
await method.setDownloadRunning(false);
|
||||||
|
_current = await method.dataLocal();
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
paths = await method.androidGetExtendDirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('数据迁移'),
|
||||||
|
),
|
||||||
|
body: ContentBuilder(
|
||||||
|
future: _future,
|
||||||
|
onRefresh: () async {},
|
||||||
|
successBuilder:
|
||||||
|
(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
|
||||||
|
switch (_migrate) {
|
||||||
|
case 0:
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: Text(
|
||||||
|
"1. 为了手机数据存储空间不足, 且具有内存卡的安卓手机设计, 可将数据迁移到内存卡上。\n\n"
|
||||||
|
"2. 您在迁移之前, 请确保您的下载处于暂停状态, 或下载均已完成, 以保证您的数据完整性。\n\n"
|
||||||
|
"3. 如果迁移中断, 迁移失败, 或其他原因导致程序无法启动, 图片失效等问题, 您可在程序管理中清除本应用程序的数据, 以回复正常使用。\n\n"
|
||||||
|
"4. 如果您将数据迁移后将内存卡取出, 将会使用默认本地存储, 再次插入同一张内存卡会继续使用该储存卡, 不支持更换内存卡, 途中您若再次迁移会发生数据覆盖, 这必然会丢失一部分数据.\n\n"
|
||||||
|
"5. 您不能更改, 删除, 移动这些数据, 否则程序可能不能正常执行\n\n"
|
||||||
|
"6. 迁移成功之前一定不要退出应用程序, 也不要按返回键\n\n"
|
||||||
|
"7. 如果您已经了解此功能, 悉知文件迁移的风险, 可以在下面的按钮中选择一项执行\n\n",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: Text("当前文件储存路径 : $_current"),
|
||||||
|
),
|
||||||
|
...paths.map((e) => Container(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
child: MaterialButton(
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
textColor: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.subtitle1
|
||||||
|
?.color,
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
onPressed: () async {
|
||||||
|
if (!await confirmDialog(context, "文件迁移",
|
||||||
|
"您将要迁移到$e, 迁移过程中一定《 不 要 关 闭 程 序 》")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_migrate = 1;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
await method.migrate(e);
|
||||||
|
setState(() {
|
||||||
|
_migrate = 2;
|
||||||
|
});
|
||||||
|
} catch (ex, tr) {
|
||||||
|
_message = "$ex\n$tr\n";
|
||||||
|
setState(() {
|
||||||
|
_migrate = 3;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text("迁移到 $e"),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
case 1:
|
||||||
|
return ContentLoading(label: "迁移中");
|
||||||
|
case 2:
|
||||||
|
return Center(child: Text("迁移成功 您需要关闭应用程序重新启动"));
|
||||||
|
case 3:
|
||||||
|
return Center(child: Text("迁移失败\n$_message"));
|
||||||
|
default:
|
||||||
|
throw "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import 'package:pikapi/basic/config/VolumeController.dart';
|
||||||
import 'package:pikapi/screens/components/NetworkSetting.dart';
|
import 'package:pikapi/screens/components/NetworkSetting.dart';
|
||||||
|
|
||||||
import 'CleanScreen.dart';
|
import 'CleanScreen.dart';
|
||||||
|
import 'MigrateScreen.dart';
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
@ -153,13 +154,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
title: Text("文件迁移"),
|
title: Text("文件迁移"),
|
||||||
subtitle: Text("更换您的数据文件夹"),
|
subtitle: Text("更换您的数据文件夹"),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
var f = confirmDialog(
|
var f = await confirmDialog(
|
||||||
context,
|
context,
|
||||||
"文件迁移",
|
"文件迁移",
|
||||||
"为了手机数据存储空间不足, 且具有内存卡的手机设计, 可将数据迁移到内存卡上。"
|
"此功能菜单保存后, 需要重启程序, 您确认吗"
|
||||||
" 您在迁移之前, 请确保您的下载处于暂停状态, 或下载均已完成, 已保证您的数据完整性。"
|
|
||||||
" 如果迁移中断或其他原因导致程序无法启动, 图片失效等问题, 您可在程序管理中清除本应用程序的数据, 以回复正常使用。",
|
|
||||||
);
|
);
|
||||||
|
if (f) {
|
||||||
|
Navigator.of(context).pushAndRemoveUntil(
|
||||||
|
MaterialPageRoute(builder: (BuildContext context) {
|
||||||
|
return MigrateScreen();
|
||||||
|
}),
|
||||||
|
(route) => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue