原神切服器 2.0

本文 在此之前仅在 吾爱破解论坛 与 Anyeの小站 发布。

原神切服器(安卓端)(支持鸿蒙)重磅更新,现已支持原神 国服B服小米服国际服,四服的通用数据转换,以此来实现四个软件共同使用一份游戏数据,避免手机内存不够的问题。

本次更新解决的问题:

  1. 解除了对 Shizuku 的依赖
    虽然 Shizuku 大佬的工具很好用,但是接到反馈得知这样的操作还是太过于复杂了,本次重构采用SAF框架进行文件操作,实现了更低权限的使用,保障用户数据安全。

  2. 实现了自动判断
    自主判断用户的手机上有哪些服务器启动器,并只显示这些启动器的切换项,避免了误触。

  3. 采用下拉菜单确认的方式
    提高人机交互体验。

  4. 打包时降低了应用权限
    仅保留必要权限。

本版本存在的问题
1.作者水平太低,采用SAF框架后暂时无法实现自动判断完整数据客户端,需要用户自行选择,增大了丢数据的风险。

2.每次启动的时候,都需要进行一次SAF框架授权,有些烦。还是水平太低,不会写固化权限,求大佬帮忙

本版本食用说明

  1. 下载安装原神切服器2.0,见文章末尾

  2. 启动后,授予显示在应用上方权限,该权限是用来显示切服器SAF框架授权界面的,允许即可。

  3. 下拉菜单第一项选择当前有完整游戏数据的客户端。第二项,选择想要切换的服务器,点击确定即可。

打包软件下载地址:(使用AutojsPro9打包)

Github

蓝奏云:密码:gvp0

源码

"ui";
importClass(java.io.File);
importClass(java.nio.file.Paths);
importClass(java.nio.file.Files);
importClass(java.lang.StringBuilder);
importClass(android.provider.DocumentsContract);
importClass(android.content.Intent);
importClass(android.net.Uri);
importClass(Packages.androidx.documentfile.provider.DocumentFile)

//跳转获取授权
function getPermission(activity) {
    let url = Uri.parse("content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fdata");
    let intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION |
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
        Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
        Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, url);
    activity.startActivityForResult(intent, 11);
}

//判断是否有授权
function hasPermission() {
    let url = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata/document/primary%3AAndroid%2Fdata");
    let documentFile = DocumentFile.fromTreeUri(activity, url);
    return documentFile.canRead() && documentFile.canWrite();
}

//获取data目录的document对象
function get_data_documentFile(activity) {
    let url = Uri.parse('content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata/document/primary%3AAndroid%2Fdata');
    return DocumentFile.fromTreeUri(activity, url);
}

//path路径转uri对象
function path_to_url(path) {
    let paths = path.replace("/storage/emulated/0/Android/data", "").split("/");
    let stringBuilder = new StringBuilder("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata/document/primary%3AAndroid%2Fdata");
    for (let i = 0; i < paths.length; i++) {
        if (paths[i].length === 0) continue;
        stringBuilder.append("%2F").append(paths[i]);
    }
    return Uri.parse(stringBuilder.toString());
}

//拷贝documet对象到文件
function saveDocumentFile(activity, documentFile, path, callback) {
    threads.start(function () {
        let inp;
        try {
            inp = getInput(activity, documentFile);
            Files.copy(inp, Paths.get(path));
            callback(true);
        } catch (err) {
            log(err);
            callback(false);
        } finally {
            inp.close();
        }
    });
}

function getInput(activity, documentFile) {
    if (documentFile.isFile()) {
        return activity.getContentResolver().openInputStream(documentFile.getUri());
    }
    return null;
}
//获取path指定的文件document对象
function getDocumentFile(activity, path) {
    let url = path_to_url(path);
    return DocumentFile.fromSingleUri(activity, url);
}

if (device.sdkInt >= 30) {
    if (!hasPermission(context)) {
        dialogs.alert('提示', '从安卓11开始,谷歌限制了Android/data目录的访问权限,' +
            '普通应用无法直接使用java.io访问该目录的文件,' +
            '但是可以通过安卓提供的SAF框架读取文件,需要跳转页面授权').then(() => {
                getPermission(activity);
            })
    }
}


const dataDir = get_data_documentFile(context);

var hasapp = []
var hasname = []
if (app.getAppName("com.miHoYo.Yuanshen") == "原神") {
    hasapp.push("com.miHoYo.Yuanshen")
    hasname.push("国服")
}
if (app.getAppName("com.miHoYo.ys.bilibili") == "原神") {
    hasapp.push("com.miHoYo.ys.bilibili")
    hasname.push("B服")
}
if (app.getAppName("com.miHoYo.ys.mi") == "原神") {
    hasapp.push("com.miHoYo.ys.mi")
    hasname.push("小米服")
}
if (app.getAppName("com.miHoYo.GenshinImpact") == "原神") {
    hasapp.push("com.miHoYo.GenshinImpact")
    hasname.push("国际服")
}

ui.layout(
    <frame>
        <vertical>
            <appbar>
                <toolbar id="title" title="原神切服器" />
            </appbar>
            <text textSize="18sp" textColor="#000000" margin="20" textStyle="bold">
                使用前需注意:
            </text>
            <text textSize="18sp" textColor="#000000" margin="20" >
                1.请选择您当前完整安装的游戏客户端,如果您的手机上有多个完整的游戏客户端,那么请随意选择一个。“完整”的游戏客户端是指您可以正常进入游戏的客户端。
            </text>
            <text textSize="18sp" textColor="#ff0000" margin="20" >
                请务必确保您选择的客户端是完整的,否则会导致游戏数据丢失。
            </text>
            <spinner id="old" />

            <text textSize="18sp" textColor="#000000" margin="20" >
                2.本切换器将删除掉其余的游戏数据,只保留一份完整的游戏数据。请选择您想要切换的客户端:
            </text>
            <spinner id="new" />
            <button id="ok">确定</button>
        </vertical>
    </frame>
);

$ui.old.attr('entries', hasname.join('|'))
$ui.new.attr('entries', hasname.join('|'))

ui.ok.on("click", () => {
    var i = ui.old.getSelectedItemPosition();
    var j = ui.new.getSelectedItemPosition();
    if (i == j) {
        toast("请选择不同的客户端");
        return;
    }
    var oldapp = hasapp[i];
    var newapp = hasapp[j];

    for (var k = 0; k < hasapp.length; k++) {
        if (k != i) {
            const delDir = dataDir.findFile(hasapp[k]);
            if (delDir != null)
                delDir.delete();
        }
    }
    const oldDir = dataDir.findFile(oldapp);
    const newDir = oldDir.renameTo(newapp);
    launch(hasapp[j])
    toast("已切换为" + hasname[j]);
});

你可能感兴趣的:(javascript)