mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-02 17:07:41 +08:00
新增:一键克隆增加离线模式(导出备份json文件到Download目录,其他机器读取文件导入)
优化:一键克隆机制优化(替换db文件→操作现有db)
This commit is contained in:
parent
60dde070b5
commit
31017609f9
@ -1,28 +1,32 @@
|
||||
package com.idormy.sms.forwarder;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.hjq.permissions.OnPermissionCallback;
|
||||
import com.hjq.permissions.Permission;
|
||||
import com.hjq.permissions.XXPermissions;
|
||||
import com.hjq.toast.ToastUtils;
|
||||
import com.idormy.sms.forwarder.model.vo.CloneInfoVo;
|
||||
import com.idormy.sms.forwarder.receiver.BaseServlet;
|
||||
import com.idormy.sms.forwarder.receiver.RebootBroadcastReceiver;
|
||||
import com.idormy.sms.forwarder.sender.HttpServer;
|
||||
import com.idormy.sms.forwarder.utils.BackupDbTask;
|
||||
import com.idormy.sms.forwarder.utils.CloneUtils;
|
||||
import com.idormy.sms.forwarder.utils.Define;
|
||||
import com.idormy.sms.forwarder.utils.DownloadUtil;
|
||||
import com.idormy.sms.forwarder.utils.FileUtils;
|
||||
import com.idormy.sms.forwarder.utils.HttpUtil;
|
||||
import com.idormy.sms.forwarder.utils.NetUtil;
|
||||
import com.idormy.sms.forwarder.utils.SettingUtil;
|
||||
@ -31,6 +35,7 @@ import com.idormy.sms.forwarder.view.IPEditText;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -47,37 +52,19 @@ public class CloneActivity extends AppCompatActivity {
|
||||
private final String TAG = "CloneActivity";
|
||||
private Context context;
|
||||
private String serverIp;
|
||||
public static final String DATABASE_NAME = "sms_forwarder.db";
|
||||
private String backupPath;
|
||||
private final String backupFile = "SmsForwarder.json";
|
||||
private IPEditText textServerIp;
|
||||
private TextView sendTxt;
|
||||
private TextView receiveTxt;
|
||||
private TextView backupPathTxt;
|
||||
private Button sendBtn;
|
||||
public static final int TOAST = 0x9731994;
|
||||
public static final int DOWNLOAD = 0x9731995;
|
||||
|
||||
//消息处理者,创建一个Handler的子类对象,目的是重写Handler的处理消息的方法(handleMessage())
|
||||
@SuppressWarnings("deprecation")
|
||||
@SuppressLint("HandlerLeak")
|
||||
private final Handler handError = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg.what == TOAST) {
|
||||
ToastUtils.delayedShow(msg.getData().getString("DATA"), 3000);
|
||||
} else if (msg.what == DOWNLOAD) {
|
||||
String savePath = context.getCacheDir().getPath() + File.separator + BackupDbTask.BACKUP_FILE;
|
||||
Log.d(TAG, savePath);
|
||||
downloadFile(msg.getData().getString("URL"), context.getCacheDir().getPath(), BackupDbTask.BACKUP_FILE, msg.getData().getString("INFO"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Log.d(TAG, "onCreate");
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
context = CloneActivity.this;
|
||||
|
||||
setContentView(R.layout.activity_clone);
|
||||
Log.d(TAG, "onCreate: " + RebootBroadcastReceiver.class.getName());
|
||||
|
||||
@ -92,6 +79,41 @@ public class CloneActivity extends AppCompatActivity {
|
||||
super.onStart();
|
||||
Log.d(TAG, "onStart");
|
||||
|
||||
backupPathTxt = findViewById(R.id.backupPathTxt);
|
||||
// 申请储存权限
|
||||
XXPermissions.with(this).permission(Permission.Group.STORAGE).request(new OnPermissionCallback() {
|
||||
@Override
|
||||
public void onGranted(List<String> permissions, boolean all) {
|
||||
backupPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
|
||||
backupPathTxt.setText(backupPath + File.separator + backupFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDenied(List<String> permissions, boolean never) {
|
||||
if (never) {
|
||||
ToastUtils.show(R.string.toast_denied_never);
|
||||
// 如果是被永久拒绝就跳转到应用权限系统设置页面
|
||||
XXPermissions.startPermissionActivity(CloneActivity.this, permissions);
|
||||
} else {
|
||||
ToastUtils.show(R.string.toast_denied);
|
||||
}
|
||||
backupPathTxt.setText("未授权储存权限,该功能无法使用!");
|
||||
}
|
||||
});
|
||||
|
||||
LinearLayout layoutNetwork = findViewById(R.id.layoutNetwork);
|
||||
LinearLayout layoutOffline = findViewById(R.id.layoutOffline);
|
||||
final RadioGroup radioGroupTypeCheck = findViewById(R.id.radioGroupTypeCheck);
|
||||
radioGroupTypeCheck.setOnCheckedChangeListener((group, checkedId) -> {
|
||||
if (checkedId == R.id.btnTypeOffline) {
|
||||
layoutNetwork.setVisibility(View.GONE);
|
||||
layoutOffline.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
layoutNetwork.setVisibility(View.VISIBLE);
|
||||
layoutOffline.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
sendBtn = findViewById(R.id.sendBtn);
|
||||
sendTxt = findViewById(R.id.sendTxt);
|
||||
TextView ipText = findViewById(R.id.ipText);
|
||||
@ -110,18 +132,14 @@ public class CloneActivity extends AppCompatActivity {
|
||||
sendBtn.setText(R.string.send);
|
||||
sendTxt.setText(R.string.server_has_stopped);
|
||||
}
|
||||
//noinspection CommentedOutCode
|
||||
|
||||
//发送
|
||||
sendBtn.setOnClickListener(v -> {
|
||||
if (!HttpServer.asRunning() && NetUtil.NETWORK_WIFI != NetUtil.getNetWorkStatus()) {
|
||||
Toast(handError, TAG, getString(R.string.no_wifi_network));
|
||||
ToastUtils.show(getString(R.string.no_wifi_network));
|
||||
return;
|
||||
}
|
||||
|
||||
//备份文件
|
||||
//BackupDbTask task = new BackupDbTask(this);
|
||||
//String backup_version = task.doInBackground(BackupDbTask.COMMAND_BACKUP);
|
||||
//Log.d(TAG, "backup_version = " + backup_version);
|
||||
|
||||
SettingUtil.switchEnableHttpServer(!SettingUtil.getSwitchEnableHttpServer());
|
||||
if (!HttpServer.update()) {
|
||||
SettingUtil.switchEnableHttpServer(!SettingUtil.getSwitchEnableHttpServer());
|
||||
@ -138,23 +156,24 @@ public class CloneActivity extends AppCompatActivity {
|
||||
}
|
||||
});
|
||||
|
||||
//接收
|
||||
receiveBtn.setOnClickListener(v -> {
|
||||
if (HttpServer.asRunning()) {
|
||||
receiveTxt.setText(R.string.sender_cannot_receive);
|
||||
Toast(handError, TAG, getString(R.string.sender_cannot_receive));
|
||||
ToastUtils.show(getString(R.string.sender_cannot_receive));
|
||||
return;
|
||||
}
|
||||
|
||||
if (NetUtil.NETWORK_WIFI != NetUtil.getNetWorkStatus()) {
|
||||
receiveTxt.setText(R.string.no_wifi_network);
|
||||
Toast(handError, TAG, getString(R.string.no_wifi_network));
|
||||
ToastUtils.show(getString(R.string.no_wifi_network));
|
||||
return;
|
||||
}
|
||||
|
||||
serverIp = textServerIp.getIP();
|
||||
if (serverIp == null || serverIp.isEmpty()) {
|
||||
receiveTxt.setText(R.string.invalid_server_ip);
|
||||
Toast(handError, TAG, getString(R.string.invalid_server_ip));
|
||||
ToastUtils.show(getString(R.string.invalid_server_ip));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -187,7 +206,7 @@ public class CloneActivity extends AppCompatActivity {
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Call call, @NonNull final IOException e) {
|
||||
Toast(handError, TAG, getString(R.string.tips_get_info_failed));
|
||||
ToastUtils.show(getString(R.string.tips_get_info_failed));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -196,7 +215,7 @@ public class CloneActivity extends AppCompatActivity {
|
||||
Log.d(TAG, "Response:" + response.code() + "," + responseStr);
|
||||
|
||||
if (TextUtils.isEmpty(responseStr)) {
|
||||
Toast(handError, TAG, getString(R.string.tips_get_info_failed));
|
||||
ToastUtils.show(getString(R.string.tips_get_info_failed));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -205,27 +224,68 @@ public class CloneActivity extends AppCompatActivity {
|
||||
Log.d(TAG, cloneInfoVo.toString());
|
||||
|
||||
if (!SettingUtil.getVersionName().equals(cloneInfoVo.getVersionName())) {
|
||||
Toast(handError, TAG, getString(R.string.tips_versions_inconsistent));
|
||||
ToastUtils.show(getString(R.string.tips_versions_inconsistent));
|
||||
return;
|
||||
}
|
||||
|
||||
//下载备份文件
|
||||
Message msg = new Message();
|
||||
msg.what = DOWNLOAD;
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("URL", requestUrl);
|
||||
bundle.putString("INFO", responseStr);
|
||||
msg.setData(bundle);
|
||||
handError.sendMessage(msg);
|
||||
if (CloneUtils.restoreSettings(cloneInfoVo)) {
|
||||
ToastUtils.show(getString(R.string.tips_clone_done));
|
||||
} else {
|
||||
ToastUtils.show(getString(R.string.tips_clone_failed));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Toast(handError, TAG, getString(R.string.tips_clone_failed) + e.getMessage());
|
||||
ToastUtils.show(getString(R.string.tips_clone_failed) + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Button exportBtn = findViewById(R.id.exportBtn);
|
||||
TextView exportTxt = findViewById(R.id.exportTxt);
|
||||
Button importBtn = findViewById(R.id.importBtn);
|
||||
TextView importTxt = findViewById(R.id.importTxt);
|
||||
|
||||
//导出
|
||||
exportBtn.setOnClickListener(v -> {
|
||||
if (FileUtils.writeFileR(CloneUtils.exportSettings(), backupPath, backupFile, true)) {
|
||||
ToastUtils.show("导出配置成功!");
|
||||
} else {
|
||||
exportTxt.setText("导出失败,请检查写入权限!");
|
||||
ToastUtils.show("导出失败,请检查写入权限!");
|
||||
}
|
||||
});
|
||||
|
||||
//导入
|
||||
importBtn.setOnClickListener(v -> {
|
||||
try {
|
||||
String responseStr = FileUtils.readFileI(backupPath, backupFile);
|
||||
if (TextUtils.isEmpty(responseStr)) {
|
||||
ToastUtils.show(getString(R.string.tips_get_info_failed));
|
||||
return;
|
||||
}
|
||||
|
||||
CloneInfoVo cloneInfoVo = JSON.parseObject(responseStr, CloneInfoVo.class);
|
||||
Log.d(TAG, Objects.requireNonNull(cloneInfoVo).toString());
|
||||
|
||||
if (!SettingUtil.getVersionName().equals(cloneInfoVo.getVersionName())) {
|
||||
ToastUtils.show(getString(R.string.tips_versions_inconsistent));
|
||||
return;
|
||||
}
|
||||
|
||||
if (CloneUtils.restoreSettings(cloneInfoVo)) {
|
||||
ToastUtils.show(getString(R.string.tips_clone_done));
|
||||
} else {
|
||||
ToastUtils.show(getString(R.string.tips_clone_failed));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
importTxt.setText("还原失败:" + e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@ -238,82 +298,4 @@ public class CloneActivity extends AppCompatActivity {
|
||||
ipText.setText(getString(R.string.local_ip) + serverIp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*
|
||||
* @param url 下载链接
|
||||
*/
|
||||
public void downloadFile(String url, final String destFileDir, final String destFileName, final String cloneInfo) {
|
||||
ProgressDialog progressDialog = new ProgressDialog(context);
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progressDialog.setTitle(getString(R.string.tips_downloading));
|
||||
progressDialog.setMessage(getString(R.string.tips_please_wait));
|
||||
progressDialog.setProgress(0);
|
||||
progressDialog.setMax(100);
|
||||
progressDialog.show();
|
||||
progressDialog.setCancelable(false);
|
||||
DownloadUtil.get().download(url, destFileDir, destFileName, new DownloadUtil.OnDownloadListener() {
|
||||
@Override
|
||||
public void onDownloadSuccess(File file) {
|
||||
if (progressDialog.isShowing()) {
|
||||
Toast(handError, TAG, getString(R.string.tips_download_done));
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
//下载完成进行相关逻辑操作
|
||||
Log.d(TAG, file.getPath());
|
||||
|
||||
//还原数据库
|
||||
BackupDbTask task = new BackupDbTask(context);
|
||||
String backup_version = task.doInBackground(BackupDbTask.COMMAND_RESTORE);
|
||||
Log.d(TAG, "backup_version = " + backup_version);
|
||||
|
||||
//应用配置
|
||||
CloneInfoVo cloneInfoVo = JSON.parseObject(cloneInfo, CloneInfoVo.class);
|
||||
System.out.println(cloneInfoVo.toString());
|
||||
SettingUtil.init(context);
|
||||
SettingUtil.switchEnableSms(cloneInfoVo.isEnableSms());
|
||||
SettingUtil.switchEnablePhone(cloneInfoVo.isEnablePhone());
|
||||
SettingUtil.switchCallType1(cloneInfoVo.isCallType1());
|
||||
SettingUtil.switchCallType2(cloneInfoVo.isCallType2());
|
||||
SettingUtil.switchCallType3(cloneInfoVo.isCallType3());
|
||||
SettingUtil.switchEnableAppNotify(cloneInfoVo.isEnableAppNotify());
|
||||
SettingUtil.switchCancelAppNotify(cloneInfoVo.isCancelAppNotify());
|
||||
SettingUtil.smsHubApiUrl(cloneInfoVo.getSmsHubApiUrl());
|
||||
SettingUtil.setBatteryLevelAlarmMin(cloneInfoVo.getBatteryLevelAlarmMin());
|
||||
SettingUtil.setBatteryLevelAlarmMax(cloneInfoVo.getBatteryLevelAlarmMax());
|
||||
SettingUtil.switchBatteryLevelAlarmOnce(cloneInfoVo.isBatteryLevelAlarmOnce());
|
||||
SettingUtil.setRetryTimes(cloneInfoVo.getRetryTimes());
|
||||
SettingUtil.setDelayTime(cloneInfoVo.getDelayTime());
|
||||
SettingUtil.switchSmsTemplate(cloneInfoVo.isEnableSmsTemplate());
|
||||
SettingUtil.setSmsTemplate(cloneInfoVo.getSmsTemplate());
|
||||
|
||||
Toast(handError, TAG, getString(R.string.tips_clone_done));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloading(int progress) {
|
||||
progressDialog.setProgress(progress);
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void onDownloadFailed(Exception e) {
|
||||
//下载异常进行相关提示操作
|
||||
Log.e(TAG, getString(R.string.tips_download_failed) + e.getMessage());
|
||||
Toast(handError, TAG, getString(R.string.tips_download_failed) + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void Toast(Handler handError, String Tag, String data) {
|
||||
Log.i(Tag, data);
|
||||
if (handError != null) {
|
||||
Message msg = new Message();
|
||||
msg.what = TOAST;
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("DATA", data);
|
||||
msg.setData(bundle);
|
||||
handError.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,11 @@ package com.idormy.sms.forwarder.model.vo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.idormy.sms.forwarder.model.RuleModel;
|
||||
import com.idormy.sms.forwarder.model.SenderModel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@ -28,7 +32,8 @@ public class CloneInfoVo implements Serializable {
|
||||
private int delayTime;
|
||||
private boolean enableSmsTemplate;
|
||||
private String smsTemplate;
|
||||
private String backupVersion;
|
||||
private List<SenderModel> senderList;
|
||||
private List<RuleModel> ruleList;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@ -51,7 +56,8 @@ public class CloneInfoVo implements Serializable {
|
||||
", delayTime=" + delayTime +
|
||||
", enableSmsTemplate=" + enableSmsTemplate +
|
||||
", smsTemplate=" + smsTemplate +
|
||||
", backupVersion=" + backupVersion +
|
||||
", senderList=" + senderList.toString() +
|
||||
", ruleList=" + ruleList.toString() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import com.alibaba.fastjson.TypeReference;
|
||||
import com.alibaba.fastjson.util.IOUtils;
|
||||
import com.idormy.sms.forwarder.model.vo.ResVo;
|
||||
import com.idormy.sms.forwarder.model.vo.SmsHubVo;
|
||||
import com.idormy.sms.forwarder.utils.BackupDbTask;
|
||||
import com.idormy.sms.forwarder.utils.CloneUtils;
|
||||
import com.idormy.sms.forwarder.utils.SettingUtil;
|
||||
import com.idormy.sms.forwarder.utils.SmsHubActionHandler;
|
||||
|
||||
@ -20,14 +20,11 @@ import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.util.HashMap;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
@ -118,7 +115,7 @@ public class BaseServlet extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//发送短信api
|
||||
private void send_api(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
resp.setCharacterEncoding("utf-8");
|
||||
PrintWriter writer = resp.getWriter();
|
||||
@ -170,40 +167,15 @@ public class BaseServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
//一键克隆——查询接口
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void clone_api(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
resp.setCharacterEncoding("utf-8");
|
||||
PrintWriter writer = resp.getWriter();
|
||||
BufferedReader reader = req.getReader();
|
||||
|
||||
try {
|
||||
//备份文件
|
||||
BackupDbTask task = new BackupDbTask(context);
|
||||
String backup_version = task.doInBackground(BackupDbTask.COMMAND_BACKUP);
|
||||
Log.d(TAG, "backup_version = " + backup_version);
|
||||
|
||||
Map msgMap = new HashMap();
|
||||
msgMap.put("versionCode", SettingUtil.getVersionCode());
|
||||
msgMap.put("versionName", SettingUtil.getVersionName());
|
||||
msgMap.put("enableSms", SettingUtil.getSwitchEnableSms());
|
||||
msgMap.put("enablePhone", SettingUtil.getSwitchEnablePhone());
|
||||
msgMap.put("callType1", SettingUtil.getSwitchCallType1());
|
||||
msgMap.put("callType2", SettingUtil.getSwitchCallType2());
|
||||
msgMap.put("callType3", SettingUtil.getSwitchCallType3());
|
||||
msgMap.put("enableAppNotify", SettingUtil.getSwitchEnableAppNotify());
|
||||
msgMap.put("cancelAppNotify", SettingUtil.getSwitchCancelAppNotify());
|
||||
msgMap.put("smsHubApiUrl", SettingUtil.getSmsHubApiUrl());
|
||||
msgMap.put("batteryLevelAlarmMin", SettingUtil.getBatteryLevelAlarmMin());
|
||||
msgMap.put("batteryLevelAlarmMax", SettingUtil.getBatteryLevelAlarmMax());
|
||||
msgMap.put("batteryLevelAlarmOnce", SettingUtil.getBatteryLevelAlarmOnce());
|
||||
msgMap.put("retryTimes", SettingUtil.getRetryTimes());
|
||||
msgMap.put("delayTime", SettingUtil.getDelayTime());
|
||||
msgMap.put("enableSmsTemplate", SettingUtil.getSwitchSmsTemplate());
|
||||
msgMap.put("smsTemplate", SettingUtil.getSmsTemplate());
|
||||
msgMap.put("backupVersion", backup_version);
|
||||
|
||||
resp.setContentType("application/json;charset=utf-8");
|
||||
String text = JSON.toJSONString(msgMap);
|
||||
writer.println(text);
|
||||
String json = CloneUtils.exportSettings();
|
||||
writer.println(json);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
printErrMsg(resp, writer, e);
|
||||
@ -215,23 +187,17 @@ public class BaseServlet extends HttpServlet {
|
||||
|
||||
//一键克隆——下载接口
|
||||
private void clone(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
File file = new File(context.getCacheDir().getPath() + File.separator + BackupDbTask.BACKUP_FILE);
|
||||
resp.addHeader("Content-Disposition", "attachment;filename=" + BackupDbTask.BACKUP_FILE);
|
||||
resp.addHeader("Content-Disposition", "attachment;filename=" + "SmsForwarder.json");
|
||||
ServletOutputStream outputStream = resp.getOutputStream();
|
||||
InputStream inputStream = new FileInputStream(file);
|
||||
try {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int size;
|
||||
while ((size = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, size);
|
||||
}
|
||||
String json = CloneUtils.exportSettings();
|
||||
outputStream.write(json.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
String text = "Internal server error: " + e.getMessage();
|
||||
Log.e(TAG, text);
|
||||
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
IOUtils.close(inputStream);
|
||||
IOUtils.close(outputStream);
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ public class SenderBarkMsg extends SenderBaseMsg {
|
||||
|
||||
static final String TAG = "SenderBarkMsg";
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
public static void sendMsg(final long logId, final Handler handError, final RetryIntercepter retryInterceptor, BarkSettingVo barkSettingVo, String title, String content, String groupName) throws Exception {
|
||||
Log.i(TAG, "sendMsg barkServer:" + barkSettingVo.toString() + " title:" + title + " content:" + content);
|
||||
|
||||
|
@ -22,6 +22,7 @@ import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
public class SenderGotifyMsg extends SenderBaseMsg {
|
||||
|
||||
static final String TAG = "SenderGotifyMsg";
|
||||
|
@ -24,7 +24,7 @@ import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "deprecation", "RedundantThrows"})
|
||||
public class SenderPushPlusMsg extends SenderBaseMsg {
|
||||
|
||||
static final String TAG = "SenderPushPlusMsg";
|
||||
|
@ -26,7 +26,7 @@ import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "deprecation", "RedundantThrows"})
|
||||
public class SenderQyWxAppMsg extends SenderBaseMsg {
|
||||
|
||||
static final String TAG = "SenderQyWxAppMsg";
|
||||
|
@ -23,7 +23,7 @@ import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
|
||||
@SuppressWarnings({"rawtypes", "unchecked", "deprecation", "RedundantThrows"})
|
||||
public class SenderQyWxGroupRobotMsg extends SenderBaseMsg {
|
||||
|
||||
static final String TAG = "SenderQyWxGroupRobotMsg";
|
||||
|
@ -20,6 +20,7 @@ import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
public class SenderServerChanMsg extends SenderBaseMsg {
|
||||
|
||||
static final String TAG = "SenderServerChanMsg";
|
||||
|
@ -8,6 +8,7 @@ import com.idormy.sms.forwarder.utils.LogUtil;
|
||||
import com.idormy.sms.forwarder.utils.SimUtil;
|
||||
import com.idormy.sms.forwarder.utils.SmsUtil;
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
public class SenderSmsMsg extends SenderBaseMsg {
|
||||
|
||||
static final String TAG = "SenderSmsMsg";
|
||||
|
@ -46,9 +46,13 @@ public class SenderUtil {
|
||||
values.put(SenderTable.SenderEntry.COLUMN_NAME_STATUS, senderModel.getStatus());
|
||||
values.put(SenderTable.SenderEntry.COLUMN_NAME_JSON_SETTING, senderModel.getJsonSetting());
|
||||
|
||||
// Insert the new row, returning the primary key value of the new row
|
||||
|
||||
return db.insert(SenderTable.SenderEntry.TABLE_NAME, null, values);
|
||||
if (null != senderModel.getId()) {
|
||||
values.put(BaseColumns._ID, senderModel.getId());
|
||||
return db.replace(SenderTable.SenderEntry.TABLE_NAME, null, values);
|
||||
} else {
|
||||
// Insert the new row, returning the primary key value of the new row
|
||||
return db.insert(SenderTable.SenderEntry.TABLE_NAME, null, values);
|
||||
}
|
||||
}
|
||||
|
||||
public static long updateSender(SenderModel senderModel) {
|
||||
|
@ -1,117 +0,0 @@
|
||||
package com.idormy.sms.forwarder.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
@SuppressWarnings({"ResultOfMethodCallIgnored", "CommentedOutCode"})
|
||||
public class BackupDbTask {
|
||||
private static final String TAG = "BackupDbTask";
|
||||
public static final String COMMAND_BACKUP = "backupDatabase";
|
||||
public static final String COMMAND_RESTORE = "restoreDatabase";
|
||||
public final static String BACKUP_FOLDER = "SmsForwarder";
|
||||
public final static String BACKUP_FILE = "SmsForwarder.zip";
|
||||
public String backup_version;
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Context mContext;
|
||||
|
||||
public BackupDbTask(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
private static File getExternalStoragePublicDir() {
|
||||
// /sdcard/SmsForwarder/
|
||||
String path = Environment.getExternalStorageDirectory() + File.separator + BACKUP_FOLDER + File.separator;
|
||||
File dir = new File(path);
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
return dir;
|
||||
}
|
||||
|
||||
public String doInBackground(String command) {
|
||||
File dbFile = mContext.getDatabasePath(DbHelper.DATABASE_NAME);// 默认路径是 /data/data/(包名)/databases/*
|
||||
File dbFile_shm = mContext.getDatabasePath(DbHelper.DATABASE_NAME + "-journal");// 默认路径是 /data/data/(包名)/databases/*
|
||||
//File dbFile_wal = mContext.getDatabasePath("event_database-wal");// 默认路径是 /data/data/(包名)/databases/*
|
||||
|
||||
String bakFolder = mContext.getCacheDir().getPath() + File.separator + BACKUP_FOLDER;
|
||||
String zipFile = mContext.getCacheDir().getPath() + File.separator + BACKUP_FILE;
|
||||
Log.d(TAG, "备份目录名:" + bakFolder + ",备份文件名:" + zipFile);
|
||||
|
||||
File exportDir = new File(mContext.getCacheDir().getPath(), BACKUP_FOLDER);//直接丢在 cache 目录,可以在在关于目录下清除缓存
|
||||
if (!exportDir.exists()) exportDir.mkdirs();
|
||||
|
||||
File backup = new File(bakFolder, dbFile.getName());//备份文件与原数据库文件名一致
|
||||
File backup_shm = new File(bakFolder, dbFile_shm.getName());//备份文件与原数据库文件名一致
|
||||
//File backup_wal = new File(bakFolder, dbFile_wal.getName());//备份文件与原数据库文件名一致
|
||||
if (command.equals(COMMAND_BACKUP)) {
|
||||
try {
|
||||
//备份文件
|
||||
backup.createNewFile();
|
||||
backup_shm.createNewFile();
|
||||
//backup_wal.createNewFile();
|
||||
fileCopy(dbFile, backup);//数据库文件拷贝至备份文件
|
||||
fileCopy(dbFile_shm, backup_shm);//数据库文件拷贝至备份文件
|
||||
//fileCopy(dbFile_wal, backup_wal);//数据库文件拷贝至备份文件
|
||||
//backup.setLastModified(MyTimeUtils.getTimeLong());
|
||||
|
||||
backup_version = TimeUtil.getTimeString("yyyy.MM.dd_HH:mm:ss");
|
||||
Log.d(TAG, "backup ok! 备份目录:" + backup.getName() + "\t" + backup_version);
|
||||
|
||||
//打包文件
|
||||
ZipUtils.ZipFolder(bakFolder, zipFile);
|
||||
Log.d(TAG, "备份Zip包:" + zipFile);
|
||||
|
||||
return backup_version;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.d(TAG, "backup fail! 备份文件名:" + backup.getName());
|
||||
return null;
|
||||
}
|
||||
} else if (command.equals(COMMAND_RESTORE)) {
|
||||
try {
|
||||
//解压文件
|
||||
ZipUtils.UnZipFolder(zipFile, bakFolder);
|
||||
Log.d(TAG, "解压Zip包:" + zipFile);
|
||||
|
||||
//还原文件
|
||||
fileCopy(backup, dbFile);//备份文件拷贝至数据库文件
|
||||
fileCopy(backup_shm, dbFile_shm);//备份文件拷贝至数据库文件
|
||||
//fileCopy(backup_wal, dbFile_wal);//备份文件拷贝至数据库文件
|
||||
backup_version = TimeUtil.getTimeString(backup.lastModified(), "yyyy.MM.dd_HH:mm:ss");
|
||||
Log.d(TAG, "restore success! 数据库文件名:" + dbFile.getName() + "\t" + backup_version);
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new InterruptedIOException(e.getMessage());
|
||||
}
|
||||
|
||||
LogUtil.delLog(null, null);
|
||||
|
||||
return backup_version;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.d(TAG, "restore fail! 数据库文件名:" + dbFile.getName());
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void fileCopy(File dbFile, File backup) {
|
||||
try (FileChannel inChannel = new FileInputStream(dbFile).getChannel(); FileChannel outChannel = new FileOutputStream(backup).getChannel()) {
|
||||
inChannel.transferTo(0, inChannel.size(), outChannel);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package com.idormy.sms.forwarder.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.idormy.sms.forwarder.model.RuleModel;
|
||||
import com.idormy.sms.forwarder.model.SenderModel;
|
||||
import com.idormy.sms.forwarder.model.vo.CloneInfoVo;
|
||||
import com.idormy.sms.forwarder.sender.SenderUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 一键克隆工具类
|
||||
*/
|
||||
public class CloneUtils {
|
||||
|
||||
//导出设置
|
||||
public static String exportSettings() {
|
||||
CloneInfoVo cloneInfo = new CloneInfoVo();
|
||||
try {
|
||||
cloneInfo.setVersionCode(SettingUtil.getVersionCode());
|
||||
cloneInfo.setVersionName(SettingUtil.getVersionName());
|
||||
cloneInfo.setEnableSms(SettingUtil.getSwitchEnableSms());
|
||||
cloneInfo.setEnablePhone(SettingUtil.getSwitchEnablePhone());
|
||||
cloneInfo.setCallType1(SettingUtil.getSwitchCallType1());
|
||||
cloneInfo.setCallType2(SettingUtil.getSwitchCallType2());
|
||||
cloneInfo.setCallType3(SettingUtil.getSwitchCallType3());
|
||||
cloneInfo.setEnableAppNotify(SettingUtil.getSwitchEnableAppNotify());
|
||||
cloneInfo.setCancelAppNotify(SettingUtil.getSwitchCancelAppNotify());
|
||||
cloneInfo.setSmsHubApiUrl(SettingUtil.getSmsHubApiUrl());
|
||||
cloneInfo.setBatteryLevelAlarmMin(SettingUtil.getBatteryLevelAlarmMin());
|
||||
cloneInfo.setBatteryLevelAlarmMax(SettingUtil.getBatteryLevelAlarmMax());
|
||||
cloneInfo.setBatteryLevelAlarmOnce(SettingUtil.getBatteryLevelAlarmOnce());
|
||||
cloneInfo.setRetryTimes(SettingUtil.getRetryTimes());
|
||||
cloneInfo.setDelayTime(SettingUtil.getDelayTime());
|
||||
cloneInfo.setEnableSmsTemplate(SettingUtil.getSwitchSmsTemplate());
|
||||
cloneInfo.setSmsTemplate(SettingUtil.getSmsTemplate());
|
||||
cloneInfo.setSenderList(SenderUtil.getSender(null, null));
|
||||
cloneInfo.setRuleList(RuleUtil.getRule(null, null));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return JSON.toJSONString(cloneInfo);
|
||||
}
|
||||
|
||||
//还原设置
|
||||
public static boolean restoreSettings(CloneInfoVo cloneInfoVo) {
|
||||
|
||||
try {
|
||||
//应用配置
|
||||
//SettingUtil.init(context);
|
||||
SettingUtil.switchEnableSms(cloneInfoVo.isEnableSms());
|
||||
SettingUtil.switchEnablePhone(cloneInfoVo.isEnablePhone());
|
||||
SettingUtil.switchCallType1(cloneInfoVo.isCallType1());
|
||||
SettingUtil.switchCallType2(cloneInfoVo.isCallType2());
|
||||
SettingUtil.switchCallType3(cloneInfoVo.isCallType3());
|
||||
SettingUtil.switchEnableAppNotify(cloneInfoVo.isEnableAppNotify());
|
||||
SettingUtil.switchCancelAppNotify(cloneInfoVo.isCancelAppNotify());
|
||||
SettingUtil.smsHubApiUrl(cloneInfoVo.getSmsHubApiUrl());
|
||||
SettingUtil.setBatteryLevelAlarmMin(cloneInfoVo.getBatteryLevelAlarmMin());
|
||||
SettingUtil.setBatteryLevelAlarmMax(cloneInfoVo.getBatteryLevelAlarmMax());
|
||||
SettingUtil.switchBatteryLevelAlarmOnce(cloneInfoVo.isBatteryLevelAlarmOnce());
|
||||
SettingUtil.setRetryTimes(cloneInfoVo.getRetryTimes());
|
||||
SettingUtil.setDelayTime(cloneInfoVo.getDelayTime());
|
||||
SettingUtil.switchSmsTemplate(cloneInfoVo.isEnableSmsTemplate());
|
||||
SettingUtil.setSmsTemplate(cloneInfoVo.getSmsTemplate());
|
||||
|
||||
SenderUtil.delSender(null);
|
||||
List<SenderModel> senderList = cloneInfoVo.getSenderList();
|
||||
for (SenderModel senderModel : senderList) {
|
||||
SenderUtil.addSender(senderModel);
|
||||
}
|
||||
|
||||
RuleUtil.delRule(null);
|
||||
List<RuleModel> ruleList = cloneInfoVo.getRuleList();
|
||||
for (RuleModel ruleModel : ruleList) {
|
||||
RuleUtil.addRule(ruleModel);
|
||||
}
|
||||
|
||||
LogUtil.delLog(null, null);
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
package com.idormy.sms.forwarder.utils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class DownloadUtil {
|
||||
private static DownloadUtil downloadUtil;
|
||||
private final OkHttpClient okHttpClient;
|
||||
|
||||
public static DownloadUtil get() {
|
||||
if (downloadUtil == null) {
|
||||
downloadUtil = new DownloadUtil();
|
||||
}
|
||||
return downloadUtil;
|
||||
}
|
||||
|
||||
private DownloadUtil() {
|
||||
okHttpClient = new OkHttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url 下载连接
|
||||
* @param destFileDir 下载的文件储存目录
|
||||
* @param destFileName 下载文件名称
|
||||
* @param listener 下载监听
|
||||
*/
|
||||
public void download(final String url, final String destFileDir, final String destFileName, final OnDownloadListener listener) {
|
||||
|
||||
Request request = new Request.Builder().url(url).addHeader("Connection", "close").build();
|
||||
okHttpClient.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
||||
// 下载失败监听回调
|
||||
listener.onDownloadFailed(e);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@Override
|
||||
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
|
||||
InputStream is = null;
|
||||
byte[] buf = new byte[2048];
|
||||
int len;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
// 储存下载文件的目录
|
||||
File dir = new File(destFileDir);
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
|
||||
File file = new File(dir, destFileName);
|
||||
if (file.exists()) file.delete();
|
||||
|
||||
try {
|
||||
is = Objects.requireNonNull(response.body()).byteStream();
|
||||
long total = Objects.requireNonNull(response.body()).contentLength();
|
||||
fos = new FileOutputStream(file);
|
||||
long sum = 0;
|
||||
while ((len = is.read(buf)) != -1) {
|
||||
fos.write(buf, 0, len);
|
||||
sum += len;
|
||||
int progress = (int) (sum * 1.0f / total * 100);
|
||||
// 下载中更新进度条
|
||||
listener.onDownloading(progress);
|
||||
}
|
||||
fos.flush();
|
||||
// 下载完成
|
||||
listener.onDownloadSuccess(file);
|
||||
} catch (Exception e) {
|
||||
listener.onDownloadFailed(e);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) is.close();
|
||||
if (fos != null) fos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnDownloadListener {
|
||||
/**
|
||||
* @param file 下载成功后的文件
|
||||
*/
|
||||
void onDownloadSuccess(File file);
|
||||
|
||||
/**
|
||||
* @param progress 下载进度
|
||||
*/
|
||||
void onDownloading(int progress);
|
||||
|
||||
/**
|
||||
* @param e 下载异常信息
|
||||
*/
|
||||
void onDownloadFailed(Exception e);
|
||||
}
|
||||
}
|
253
app/src/main/java/com/idormy/sms/forwarder/utils/FileUtils.java
Normal file
253
app/src/main/java/com/idormy/sms/forwarder/utils/FileUtils.java
Normal file
@ -0,0 +1,253 @@
|
||||
package com.idormy.sms.forwarder.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/*
|
||||
* 文件读写工具类
|
||||
* external storage
|
||||
外部存储 Environment.getExternalStorageDirectory() SD根目录:/mnt/sdcard/ (6.0后写入需要用户授权)
|
||||
context.getExternalFilesDir(dir) 路径为:/mnt/sdcard/Android/data/< package name >/files/…
|
||||
context.getExternalCacheDir() 路径为:/mnt/sdcard//Android/data/< package name >/cach/…
|
||||
*
|
||||
internal storage
|
||||
内部存储
|
||||
context.getFilesDir() 路径是:/data/data/< package name >/files/…
|
||||
context.getCacheDir() 路径是:/data/data/< package name >/cach/…
|
||||
*/
|
||||
@SuppressWarnings({"UnusedReturnValue", "ResultOfMethodCallIgnored"})
|
||||
public class FileUtils {
|
||||
private static final String TAG = FileUtils.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* @return .外部储存sd卡 根路径
|
||||
*/
|
||||
public static String getRootPath() {
|
||||
// /storage/emulated/0
|
||||
return Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context .
|
||||
* @return . 外部储存sd卡 :/mnt/sdcard/Android/data/< package name >/files/…
|
||||
*/
|
||||
public static String getAppRootPth(Context context) {
|
||||
// /storage/emulated/0/Android/data/pack_name/files
|
||||
return context.getExternalFilesDir("").getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return .内部存储
|
||||
*/
|
||||
public static String getInternalPath() {
|
||||
// /data
|
||||
return Environment.getDataDirectory().getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context .
|
||||
* @return .内部储存:/data/data/< package name >/files/
|
||||
*/
|
||||
public static String getInternalAppPath(Context context) {
|
||||
return context.getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path 路径
|
||||
* @param fileName 文件名称
|
||||
* @return .
|
||||
*/
|
||||
public static boolean createFile(String path, String fileName) {
|
||||
File file = new File(path + File.separator + fileName);
|
||||
|
||||
//先创建文件夹 保证文件创建成功
|
||||
createDirs(path);
|
||||
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
//
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param folder 创建多级文件夹
|
||||
* @return .
|
||||
*/
|
||||
public static boolean createDirs(String folder) {
|
||||
File file = new File(folder);
|
||||
|
||||
if (!file.exists()) {
|
||||
boolean mkdirs = file.mkdirs();
|
||||
Log.i(TAG, "createDirs: 不存在文件夹 开始创建" + mkdirs + "--" + folder);
|
||||
return true;
|
||||
} else {
|
||||
Log.i(TAG, "createDirs: 文件夹已存在");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* =======================================文件读写=============================================
|
||||
*
|
||||
* @param content 写入字符串
|
||||
* @param path . 目录
|
||||
* @param fileName .文件名
|
||||
* @param isRewrite 是否覆盖
|
||||
*/
|
||||
//1.RandomAccessFile 读写
|
||||
public static boolean writeFileR(String content, String path, String fileName, boolean isRewrite) {
|
||||
//路径非斜杆结尾
|
||||
if (!path.endsWith("/")) path += File.separator;
|
||||
|
||||
File file = new File(path + fileName);
|
||||
|
||||
//文件目录不存在,先创建
|
||||
if (!file.exists()) createFile(path, fileName);
|
||||
|
||||
RandomAccessFile randomAccessFile;
|
||||
try {
|
||||
randomAccessFile = new RandomAccessFile(file, "rw");
|
||||
if (isRewrite) {
|
||||
randomAccessFile.setLength(content.length());
|
||||
randomAccessFile.seek(0);
|
||||
} else {
|
||||
randomAccessFile.seek(randomAccessFile.length());
|
||||
}
|
||||
randomAccessFile.write(content.getBytes());
|
||||
randomAccessFile.close();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean writeFileR(String content, String path, String fileName) {
|
||||
return writeFileR(content, path, fileName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读文件
|
||||
*
|
||||
* @param path .
|
||||
* @param fileName .
|
||||
* @return .
|
||||
*/
|
||||
public static String readFileR(String path, String fileName) {
|
||||
//路径非斜杆结尾
|
||||
if (!path.endsWith("/")) path += File.separator;
|
||||
|
||||
File file = new File(path + fileName);
|
||||
if (!file.exists()) {
|
||||
Log.i(TAG, "readFileR: return null");
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
try {
|
||||
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
|
||||
randomAccessFile.seek(0);
|
||||
byte[] buf = new byte[(int) randomAccessFile.length()];
|
||||
if (randomAccessFile.read(buf) != -1) {
|
||||
buffer.append(new String(buf));
|
||||
Log.i(TAG, "readFileR: length" + randomAccessFile.length());
|
||||
//buffer.append(new String(buf, StandardCharsets.UTF_8));
|
||||
}
|
||||
randomAccessFile.close();
|
||||
} catch (IOException e) {
|
||||
Log.i(TAG, "readFileR: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读文件
|
||||
*
|
||||
* @param path .文件路径
|
||||
* @param name .名称
|
||||
* @return .
|
||||
*/
|
||||
public static String readFileI(String path, String name) {
|
||||
//路径非斜杆结尾
|
||||
if (!path.endsWith("/")) path += File.separator;
|
||||
|
||||
//默认编码格式 StandardCharsets.UTF_8;
|
||||
File file = new File(path, name);
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
builder.append("\n");
|
||||
}
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return builder.toString();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入文件
|
||||
*
|
||||
* @param content 内容.
|
||||
* @param path 目录.
|
||||
* @param fileName 文件名 .
|
||||
*/
|
||||
public static void writeFileO(String content, String path, String fileName) {
|
||||
writeFileO(content, path, fileName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param content .
|
||||
* @param path .
|
||||
* @param fileName .
|
||||
* @param isReWrite .是否追加
|
||||
*/
|
||||
public static void writeFileO(String content, String path, String fileName, boolean isReWrite) {
|
||||
//路径非斜杆结尾
|
||||
if (!path.endsWith("/")) path += File.separator;
|
||||
|
||||
File file = new File(path + fileName);
|
||||
|
||||
//文件目录不存在,先创建
|
||||
if (!file.exists()) createFile(path, fileName);
|
||||
|
||||
try {
|
||||
FileOutputStream ops = new FileOutputStream(file, isReWrite);
|
||||
OutputStreamWriter opsw = new OutputStreamWriter(ops, StandardCharsets.UTF_8);
|
||||
// byte[] bytes = content.getBytes();
|
||||
opsw.write(content);
|
||||
opsw.close();
|
||||
ops.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -147,6 +147,7 @@ public class HttpUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
public static class Callback0 implements Callback {
|
||||
public Callback0(String tag, Lamda.Consumer<Response> onResponse, Lamda.Consumer<Exception> onFailure) {
|
||||
this.tag = tag;
|
||||
|
@ -48,9 +48,13 @@ public class RuleUtil {
|
||||
values.put(RuleTable.RuleEntry.COLUMN_REGEX_REPLACE, ruleModel.getRegexReplace());
|
||||
values.put(RuleTable.RuleEntry.COLUMN_NAME_STATUS, ruleModel.getStatus());
|
||||
|
||||
// Insert the new row, returning the primary key value of the new row
|
||||
|
||||
return db.insert(RuleTable.RuleEntry.TABLE_NAME, null, values);
|
||||
if (null != ruleModel.getId()) {
|
||||
values.put(BaseColumns._ID, ruleModel.getId());
|
||||
return db.replace(RuleTable.RuleEntry.TABLE_NAME, null, values);
|
||||
} else {
|
||||
// Insert the new row, returning the primary key value of the new row
|
||||
return db.insert(RuleTable.RuleEntry.TABLE_NAME, null, values);
|
||||
}
|
||||
}
|
||||
|
||||
public static long updateRule(RuleModel ruleModel) {
|
||||
@ -91,6 +95,10 @@ public class RuleUtil {
|
||||
|
||||
}
|
||||
|
||||
public static List<RuleModel> getRule(Long id, String key) {
|
||||
return getRule(id, key, null, null);
|
||||
}
|
||||
|
||||
public static List<RuleModel> getRule(Long id, String key, String type) {
|
||||
return getRule(id, key, type, null);
|
||||
}
|
||||
|
@ -1,112 +1,265 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:weightSum="1">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_weight="0.3"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/sendBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/send_btn"
|
||||
android:text="@string/send"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sendTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10sp"
|
||||
android:text="@string/old_mobile_phone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.1"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/server_ip"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.idormy.sms.forwarder.view.IPEditText
|
||||
android:id="@+id/textServerIp"
|
||||
<RadioGroup
|
||||
android:id="@+id/radioGroupTypeCheck"
|
||||
style="@style/rg_style"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_marginTop="5dip"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/btnTypeNetwork"
|
||||
style="@style/select_style"
|
||||
android:checked="true"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:tag="network"
|
||||
android:text="@string/network_model" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/btnTypeOffline"
|
||||
style="@style/select_style"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:tag="offline"
|
||||
android:text="@string/offline_mode" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<View
|
||||
android:id="@+id/radioGroupLine"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:background="#aadcdcdc" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNetwork"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="15dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/sendBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/send_btn"
|
||||
android:text="@string/send"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sendTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10sp"
|
||||
android:text="@string/old_mobile_phone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/server_ip"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.idormy.sms.forwarder.view.IPEditText
|
||||
android:id="@+id/textServerIp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/receiveBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/receive_btn"
|
||||
android:text="@string/receive"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiveTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10sp"
|
||||
android:text="@string/new_mobile_phone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ipText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/local_ip"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/operating_instruction"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutOffline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="15dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/exportBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/send_btn"
|
||||
android:text="@string/export"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exportTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10sp"
|
||||
android:text="@string/old_mobile_phone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tips_backup_path"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/backupPathTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/importBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/receive_btn"
|
||||
android:text="@string/imports"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/importTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10sp"
|
||||
android:text="@string/new_mobile_phone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/operating_instruction_offline"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.3"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/receiveBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@drawable/receive_btn"
|
||||
android:text="@string/receive"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/receiveTxt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10sp"
|
||||
android:text="@string/new_mobile_phone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.2"
|
||||
android:gravity="start"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/ipText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/local_ip"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/operating_instruction"
|
||||
android:textSize="10sp"
|
||||
tools:ignore="SmallSp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -227,8 +227,11 @@
|
||||
<!--CloneActivity-->
|
||||
<string name="local_ip">Local IP:</string>
|
||||
<string name="operating_instruction">Instructions: \n[Note] The APP version of the sender and receiver must be the same!\n1. Please keep the SOURCE and DESTINATION phones in the same Wi-Fi network, and do not turn on isolation. \n2. Tap "Send" on SOURCE mobile phone, and get "server IP" \n3. After filling in "Server IP" on DESTINATION phone, tap "Receive". \n [NOTE:] sender(s), forwarding rule(s) and log(s) will be overwritten after cloning!</string> <!-- 原文是“新旧手机”,英文翻译中处理为“源”手机和“目标”手机,因为担心“新旧”的表述引起混淆(有没一种可能就是用户就是用从新手机的设备复制到旧手机上去呢?)。 -->
|
||||
<string name="operating_instruction_offline">[NOTE:] sender(s), forwarding rule(s) and log(s) will be overwritten after cloning!</string> <!-- 原文是“新旧手机”,英文翻译中处理为“源”手机和“目标”手机,因为担心“新旧”的表述引起混淆(有没一种可能就是用户就是用从新手机的设备复制到旧手机上去呢?)。 -->
|
||||
<string name="send">Start</string>
|
||||
<string name="stop">Stop</string>
|
||||
<string name="export">Export</string>
|
||||
<string name="imports">Import</string>
|
||||
<string name="old_mobile_phone">I\'m the SCOURCE phone</string>
|
||||
<string name="receive">Receive</string>
|
||||
<string name="new_mobile_phone">I\'m the DESTINATION phone</string>
|
||||
@ -393,10 +396,6 @@
|
||||
<string name="tips_compatible_solution">Compatible solution</string>
|
||||
<string name="tips_wait_3_seconds">Please wait 3 seconds after clicking start</string>
|
||||
<string name="tips_clone_done">One-click clone operation is complete! \nPlease check whether the general settings and switches are turned on!</string>
|
||||
<string name="tips_download_done">Download complete, preparing to restore data…</string>
|
||||
<string name="tips_download_failed">Download failed:</string>
|
||||
<string name="tips_downloading">Downloading</string>
|
||||
<string name="tips_please_wait">Please wait…</string>
|
||||
<string name="tips_clone_failed">One-click clone failed:</string>
|
||||
<string name="tips_versions_inconsistent">The APP versions of the sender and the receiver are inconsistent, and cannot be cloned with one click!</string>
|
||||
<string name="tips_get_info_failed">Failed to get one-click clone information from sender</string>
|
||||
@ -452,4 +451,7 @@
|
||||
<string name="tips_other_mail_type">For other email addresses, please fill in the complete email address and manually fill in the SMTP server information</string>
|
||||
<string name="optional">Optional</string>
|
||||
<string name="TelegramChatIdTips">Follow the steps in the wiki to obtain it</string>
|
||||
<string name="network_model">网络模式</string>
|
||||
<string name="offline_mode">离线模式</string>
|
||||
<string name="tips_backup_path">Backup Path:</string>
|
||||
</resources>
|
||||
|
@ -225,9 +225,12 @@
|
||||
<string name="post">POST</string>
|
||||
<string name="get">GET</string>
|
||||
<string name="local_ip">本机IP:</string>
|
||||
<string name="operating_instruction">操作说明:\n1.新旧手机连接同一个WiFi网络(禁用AP隔离)\n2.旧手机直接点【发送】按钮,获取到【服务端IP】\n3.新手机填写【服务端IP】后,点【接收】按钮\n\n注意事项:\n1.发送方与接收方的APP版本必须一致,才能克隆!\n2.新手机接收后,发送通道、转发规则将完全被覆盖,清空历史记录!\n3.主动请求、保活措施、个性设置不在克隆范围</string>
|
||||
<string name="operating_instruction">严正声明:\n该功能仅限个人新旧手机切换使用,用于非法用途后果自负!\n\n操作说明:\n1.新旧手机连接同一个WiFi网络(禁用AP隔离)\n2.旧手机直接点【发送】按钮,获取到【服务端IP】\n3.新手机填写【服务端IP】后,点【接收】按钮\n\n注意事项:\n1.发送方与接收方的APP版本必须一致,才能克隆!\n2.新手机接收后,发送通道、转发规则将完全被覆盖,清空历史记录!\n3.主动请求、保活措施、个性设置不在克隆范围</string>
|
||||
<string name="operating_instruction_offline">严正声明:\n该功能仅限个人新旧手机切换使用,用于非法用途后果自负!\n\n注意事项:\n1.发送方与接收方的APP版本必须一致,才能克隆!\n2.新手机接收后,发送通道、转发规则将完全被覆盖,清空历史记录!\n3.主动请求、保活措施、个性设置不在克隆范围</string>
|
||||
<string name="send">启动</string>
|
||||
<string name="stop">停止</string>
|
||||
<string name="export">导出</string>
|
||||
<string name="imports">导入</string>
|
||||
<string name="old_mobile_phone">我是旧手机</string>
|
||||
<string name="receive">接收</string>
|
||||
<string name="new_mobile_phone">我是新手机</string>
|
||||
@ -392,13 +395,9 @@
|
||||
<string name="tips_compatible_solution">兼容方案</string>
|
||||
<string name="tips_wait_3_seconds">点击启动后请等待3秒</string>
|
||||
<string name="tips_clone_done">一键克隆操作完成!\n请检查·通用设置·各项开关是否已开启!</string>
|
||||
<string name="tips_download_done">下载完成,正准备还原数据…</string>
|
||||
<string name="tips_download_failed">下载失败:</string>
|
||||
<string name="tips_downloading">正在下载</string>
|
||||
<string name="tips_please_wait">请稍后…</string>
|
||||
<string name="tips_clone_failed">一键克隆失败:</string>
|
||||
<string name="tips_versions_inconsistent">发送端与接收端的APP版本不一致,无法一键克隆!</string>
|
||||
<string name="tips_get_info_failed">从发送端获取一键克隆信息失败</string>
|
||||
<string name="tips_versions_inconsistent">APP版本不一致,无法一键克隆!</string>
|
||||
<string name="tips_get_info_failed">获取一键克隆信息失败</string>
|
||||
<string name="linkman">联 系 人:</string>
|
||||
<string name="via_number">来源号码:</string>
|
||||
<string name="bark_sound">消息声音</string>
|
||||
@ -451,4 +450,7 @@
|
||||
<string name="tips_other_mail_type">其他邮箱,请填写完整的邮箱地址并手动填写SMTP服务器信息</string>
|
||||
<string name="optional">可选</string>
|
||||
<string name="TelegramChatIdTips">请按照wiki中的步骤获取</string>
|
||||
<string name="network_model">网络模式</string>
|
||||
<string name="offline_mode">离线模式</string>
|
||||
<string name="tips_backup_path">备份文件存放路径:</string>
|
||||
</resources>
|
||||
|
@ -1,3 +1,3 @@
|
||||
#Fri Jul 16 10:33:23 CST 2021
|
||||
versionName=2.4.3
|
||||
versionCode=36
|
||||
versionName=2.4.4
|
||||
versionCode=37
|
||||
|
Loading…
x
Reference in New Issue
Block a user