增加配置导出导入功能(一键克隆)

This commit is contained in:
pppscn 2021-10-14 15:49:32 +08:00
parent 8a79c56fa2
commit 394ca4513c
19 changed files with 775 additions and 15 deletions

1
.gitignore vendored
View File

@ -18,3 +18,4 @@ gradle.properties
/keystore/keystore.properties
/app/release
/keystore
*.bak

View File

@ -205,7 +205,7 @@
--------
## 更新记录:PS.点击版本号下载对应的版本)
## 更新记录:
+ [v1.0.0] 优化后第一版
+ [v1.1.0] 新增在线升级、缓存清理、加入QQ群功能

View File

@ -116,7 +116,7 @@ void cmdExecute(String cmd) {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
@ -147,6 +147,8 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok:1.18.20'
//RxJava
implementation "io.reactivex.rxjava3:rxjava:3.1.1"
implementation 'io.reactivex.rxjava3:rxjava:3.1.1'
//AndroidAsync
implementation 'com.koushikdutta.async:androidasync:3.1.0'
}

View File

@ -4,6 +4,7 @@
package="com.idormy.sms.forwarder">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- 授予应用程序访问系统开机事件的权限 -->
<uses-permission
@ -77,6 +78,9 @@
<activity
android:name=".SettingActivity"
android:label="@string/setting" />
<activity
android:name=".CloneActivity"
android:label="@string/clone" />
<activity
android:name=".RuleActivity"
android:label="@string/rule_setting" />

View File

@ -0,0 +1,238 @@
package com.idormy.sms.forwarder;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.idormy.sms.forwarder.receiver.RebootBroadcastReceiver;
import com.idormy.sms.forwarder.utils.LogUtil;
import com.idormy.sms.forwarder.utils.NetUtil;
import com.idormy.sms.forwarder.view.IPEditText;
import com.koushikdutta.async.http.WebSocket;
import com.koushikdutta.async.http.server.AsyncHttpServer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class CloneActivity extends AppCompatActivity {
private final String TAG = "com.idormy.sms.forwarder.CloneActivity";
private Context context;
private boolean isRunning = false;
private String serverIp;
private final String DATABASE_NAME = "sms_forwarder.db";
@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());
}
@SuppressLint("SetTextI18n")
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
IPEditText textServerIp = findViewById(R.id.textServerIp);
List<WebSocket> _sockets = new ArrayList<>();
AsyncHttpServer server = new AsyncHttpServer();
TextView sendTxt = findViewById(R.id.sendTxt);
TextView receiveTxt = findViewById(R.id.receiveTxt);
Button sendBtn = findViewById(R.id.sendBtn);
sendBtn.setOnClickListener(v -> {
if (NetUtil.NETWORK_WIFI != NetUtil.getNetWorkStatus()) {
Toast.makeText(CloneActivity.this, R.string.no_wifi_network, Toast.LENGTH_SHORT).show();
return;
} else {
serverIp = NetUtil.getLocalIp(CloneActivity.this);
TextView ipText = findViewById(R.id.ipText);
ipText.setText(getString(R.string.local_ip) + serverIp);
}
if (!isRunning) {
isRunning = true;
server.get("/", (request, response) -> {
File file = context.getDatabasePath(DATABASE_NAME);
response.getHeaders().add("Content-Disposition", "attachment;filename=" + DATABASE_NAME);
response.sendFile(file);
});
server.listen(5000);
Toast.makeText(CloneActivity.this, R.string.server_has_started, Toast.LENGTH_SHORT).show();
sendTxt.setText(R.string.server_has_started);
textServerIp.setIP(serverIp);
sendBtn.setText(R.string.stop);
} else {
isRunning = false;
server.stop();
Toast.makeText(CloneActivity.this, R.string.server_has_stopped, Toast.LENGTH_SHORT).show();
sendTxt.setText(R.string.server_has_stopped);
textServerIp.setIP("");
sendBtn.setText(R.string.send);
}
});
Button receiveBtn = findViewById(R.id.receiveBtn);
receiveBtn.setOnClickListener(v -> {
if (isRunning) {
receiveTxt.setText(R.string.sender_cannot_receive);
Toast.makeText(CloneActivity.this, R.string.sender_cannot_receive, Toast.LENGTH_SHORT).show();
return;
}
if (NetUtil.NETWORK_WIFI != NetUtil.getNetWorkStatus()) {
receiveTxt.setText(R.string.no_wifi_network);
Toast.makeText(CloneActivity.this, R.string.no_wifi_network, Toast.LENGTH_SHORT).show();
return;
}
serverIp = textServerIp.getIP();
if (serverIp == null || serverIp.isEmpty()) {
receiveTxt.setText(R.string.invalid_server_ip);
Toast.makeText(CloneActivity.this, R.string.invalid_server_ip, Toast.LENGTH_SHORT).show();
return;
}
//下载连接
final String url = "http://" + serverIp + ":5000/";
Log.d(TAG, url);
//保存路径
final String savePath = context.getCacheDir().getPath() + File.separator + DATABASE_NAME;
Log.d(TAG, savePath);
final long startTime = System.currentTimeMillis();
Log.i(TAG, "startTime=" + startTime);
OkHttpClient okHttpClient = new OkHttpClient();
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) {
e.printStackTrace();
//Toast.makeText(CloneActivity.this, R.string.download_failed + e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
InputStream is = null;
byte[] buf = new byte[2048];
int len;
FileOutputStream fos = null;
try {
is = Objects.requireNonNull(response.body()).byteStream();
long total = Objects.requireNonNull(response.body()).contentLength();
File file = new File(savePath, url.substring(url.lastIndexOf("/") + 1));
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);
Log.e(TAG, "download progress : " + progress);
}
fos.flush();
Log.e(TAG, "download success");
Log.e(TAG, "totalTime=" + (System.currentTimeMillis() - startTime));
//Toast.makeText(CloneActivity.this, R.string.download_success, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
//Toast.makeText(CloneActivity.this, R.string.download_failed + e.getMessage(), Toast.LENGTH_SHORT).show();
} finally {
try {
if (is != null) is.close();
} catch (IOException ignored) {
}
try {
if (fos != null) fos.close();
} catch (IOException ignored) {
}
}
}
});
//TODO:替换sqlite
File dbFile = new File(savePath);
FileInputStream fis;
try {
fis = new FileInputStream(dbFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
String outFileName = context.getDatabasePath(DATABASE_NAME).getAbsolutePath();
Log.d(TAG, outFileName);
// Open the empty db as the output stream
OutputStream output;
try {
output = new FileOutputStream(outFileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
// Transfer bytes from the input file to the output file
byte[] buffer = new byte[1024];
int length;
while (true) {
try {
if (!((length = fis.read(buffer)) > 0)) break;
output.write(buffer, 0, length);
} catch (IOException e) {
e.printStackTrace();
}
}
// Close the streams
try {
output.flush();
output.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
LogUtil.delLog(null, null);
receiveTxt.setText(R.string.download_success);
});
}
@SuppressLint("SetTextI18n")
@Override
protected void onResume() {
super.onResume();
serverIp = NetUtil.getLocalIp(CloneActivity.this);
TextView ipText = findViewById(R.id.ipText);
ipText.setText(getString(R.string.local_ip) + serverIp);
}
}

View File

@ -171,6 +171,11 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I
builder.show();
}
public void toClone() {
Intent intent = new Intent(this, CloneActivity.class);
startActivity(intent);
}
public void toSetting() {
Intent intent = new Intent(this, SettingActivity.class);
startActivity(intent);
@ -217,6 +222,9 @@ public class MainActivity extends AppCompatActivity implements RefreshListView.I
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.to_clone:
toClone();
return true;
case R.id.to_setting:
toSetting();
return true;

View File

@ -4,15 +4,19 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.widget.Toast;
import com.idormy.sms.forwarder.R;
public class NetUtil {
//没有网络
private static final int NETWORK_NONE = 0;
public static final int NETWORK_NONE = 0;
//移动网络
private static final int NETWORK_MOBILE = 1;
public static final int NETWORK_MOBILE = 1;
//无线网络
private static final int NETWORK_WIFI = 2;
public static final int NETWORK_WIFI = 2;
static Boolean hasInit = false;
@SuppressLint("StaticFieldLeak")
@ -39,20 +43,31 @@ public class NetUtil {
//判断是否是wifi
if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_WIFI)) {
//返回无线网络
Toast.makeText(context, "当前处于无线网络", Toast.LENGTH_SHORT).show();
Toast.makeText(context, R.string.on_wireless_network, Toast.LENGTH_SHORT).show();
return NETWORK_WIFI;
//判断是否移动网络
} else if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_MOBILE)) {
Toast.makeText(context, "当前处于移动网络", Toast.LENGTH_SHORT).show();
Toast.makeText(context, R.string.on_mobile_network, Toast.LENGTH_SHORT).show();
//返回移动网络
return NETWORK_MOBILE;
}
} else {
//没有网络
Toast.makeText(context, "当前没有网络", Toast.LENGTH_SHORT).show();
Toast.makeText(context, R.string.no_network, Toast.LENGTH_SHORT).show();
return NETWORK_NONE;
}
//默认返回 没有网络
return NETWORK_NONE;
}
public static String getLocalIp(Context context) {
if (NETWORK_WIFI != getNetWorkStatus()) return context.getString(R.string.not_connected_wifi);
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();
if (ipAddress == 0) return context.getString(R.string.failed_to_get_ip);
return ((ipAddress & 0xff) + "." + (ipAddress >> 8 & 0xff) + "."
+ (ipAddress >> 16 & 0xff) + "." + (ipAddress >> 24 & 0xff));
}
}

View File

@ -0,0 +1,255 @@
package com.idormy.sms.forwarder.view;
import android.content.Context;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.idormy.sms.forwarder.R;
import java.util.regex.Pattern;
public class IPEditText extends LinearLayout {
//控件
private final EditText Edit1;
private final EditText Edit2;
private final EditText Edit3;
private final EditText Edit4;
private String ip1;
private String ip2;
private String ip3;
private String ip4;
public IPEditText(final Context context, AttributeSet attrs) {
super(context, attrs);
//初始化界面
View view = LayoutInflater.from(context).inflate(R.layout.iptext, this);
//绑定
Edit1 = findViewById(R.id.edit1);
Edit2 = findViewById(R.id.edit2);
Edit3 = findViewById(R.id.edit3);
Edit4 = findViewById(R.id.edit4);
//初始化函数
init(context);
}
private void init(final Context context) {
/*
监听文本得到ip段自动进入下一个输入框
*/
Edit1.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ip1 = s.toString().trim();
int lenIp1 = ip1.length();
if (lenIp1 > 0 && !Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.?$", ip1)) {
ip1 = ip1.substring(0, lenIp1 - 1);
Edit1.setText(ip1);
Edit1.setSelection(ip1.length());
Toast.makeText(context, R.string.invalid_ip, Toast.LENGTH_LONG).show();
return;
}
//非空输入 . 跳到下一个输入框
if (lenIp1 > 1 && ".".equals(ip1.substring(lenIp1 - 1))) {
ip1 = ip1.substring(0, lenIp1 - 1);
Edit1.setText(ip1);
Edit2.setFocusable(true);
Edit2.requestFocus();
return;
}
//已输3位数字跳到下一个输入框
if (lenIp1 > 2) {
Edit2.setFocusable(true);
Edit2.requestFocus();
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
Edit2.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ip2 = s.toString().trim();
int lenIp2 = ip2.length();
if (lenIp2 > 0 && !Pattern.matches("^(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.?$", ip2)) {
ip2 = ip2.substring(0, lenIp2 - 1);
Edit2.setText(ip2);
Edit2.setSelection(ip2.length());
Toast.makeText(context, R.string.invalid_ip, Toast.LENGTH_LONG).show();
return;
}
//非空输入 . 跳到下一个输入框
if (lenIp2 > 1 && ".".equals(ip2.substring(lenIp2 - 1))) {
ip2 = ip2.substring(0, lenIp2 - 1);
Edit2.setText(ip2);
Edit3.setFocusable(true);
Edit3.requestFocus();
return;
}
//已输3位数字跳到下一个输入框
if (lenIp2 > 2) {
Edit3.setFocusable(true);
Edit3.requestFocus();
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
Edit3.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ip3 = s.toString().trim();
int lenIp3 = ip3.length();
if (lenIp3 > 0 && !Pattern.matches("^(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.?$", ip3)) {
ip3 = ip3.substring(0, lenIp3 - 1);
Edit3.setText(ip3);
Edit3.setSelection(ip3.length());
Toast.makeText(context, R.string.invalid_ip, Toast.LENGTH_LONG).show();
return;
}
//非空输入 . 跳到下一个输入框
if (lenIp3 > 1 && ".".equals(ip3.substring(lenIp3 - 1))) {
ip3 = ip3.substring(0, lenIp3 - 1);
Edit3.setText(ip3);
Edit4.setFocusable(true);
Edit4.requestFocus();
return;
}
//已输3位数字跳到下一个输入框
if (lenIp3 > 2) {
Edit4.setFocusable(true);
Edit4.requestFocus();
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
Edit4.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
ip4 = s.toString().trim();
int lenIp4 = ip4.length();
if (lenIp4 > 0 && !Pattern.matches("^(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])$", ip4)) {
ip4 = ip4.substring(0, lenIp4 - 1);
Edit4.setText(ip4);
Edit4.setSelection(ip4.length());
Toast.makeText(context, R.string.invalid_ip, Toast.LENGTH_LONG).show();
return;
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
/*
监听控件空值时del键返回上一输入框
*/
Edit2.setOnKeyListener((v, keyCode, event) -> {
if (ip2 == null || ip2.isEmpty()) {
if (keyCode == KeyEvent.KEYCODE_DEL) {
Edit1.setFocusable(true);
Edit1.requestFocus();
Edit1.setSelection(ip1.length());
}
}
return false;
});
Edit3.setOnKeyListener((v, keyCode, event) -> {
if (ip3 == null || ip3.isEmpty()) {
if (keyCode == KeyEvent.KEYCODE_DEL) {
Edit2.setFocusable(true);
Edit2.requestFocus();
Edit2.setSelection(ip2.length());
}
}
return false;
});
Edit4.setOnKeyListener((v, keyCode, event) -> {
if (ip4 == null || ip4.isEmpty()) {
if (keyCode == KeyEvent.KEYCODE_DEL) {
Edit3.setFocusable(true);
Edit3.requestFocus();
Edit3.setSelection(ip3.length());
}
}
return false;
});
}
/**
* 成员函数返回整个ip地址
*/
public String getIP() {
//文本
String text;
if (TextUtils.isEmpty(ip1) || TextUtils.isEmpty(ip2)
|| TextUtils.isEmpty(ip3) || TextUtils.isEmpty(ip4)) {
text = null;
} else {
text = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
}
return text;
}
/**
* 成员函数返回整个ip地址
*/
public void setIP(String ip) {
if (ip == null || ip.isEmpty()
|| !Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ip)) {
ip1 = "";
ip2 = "";
ip3 = "";
ip4 = "";
} else {
String[] ips = ip.split("\\.");
ip1 = ips[0];
ip2 = ips[1];
ip3 = ips[2];
ip4 = ips[3];
}
Edit1.setText(ip1);
Edit2.setText(ip2);
Edit3.setText(ip3);
Edit4.setText(ip4);
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<solid android:color="@color/colorBlueGrey" />
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<solid android:color="@color/colorBlueGreyDark" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<solid android:color="@color/colorAccent" />
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="oval">
<size android:width="120dp" android:height="120dp" />
<solid android:color="@color/colorPrimaryDark" />
</shape>
</item>
</selector>

View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:weightSum="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:gravity="start"
android:orientation="horizontal">
</LinearLayout>
<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:textStyle="bold"
android:text="@string/server_ip" />
<com.idormy.sms.forwarder.view.IPEditText
android:id="@+id/textServerIp"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_alignParentLeft="true" />
</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="10sp"
android:text="@string/local_ip"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10sp"
android:text="@string/operating_instruction" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<EditText
android:id="@+id/edit1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:digits="0123456789."
android:maxLength="3"
android:text="" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/point" />
<EditText
android:id="@+id/edit2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:digits="0123456789."
android:maxLength="3"
android:text="" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/point" />
<EditText
android:id="@+id/edit3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:digits="0123456789."
android:maxLength="3"
android:text="" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:text="@string/point" />
<EditText
android:id="@+id/edit4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:inputType="number"
android:digits="0123456789."
android:maxLength="3"
android:text="" />
</merge>

View File

@ -2,6 +2,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.allmything.linkhelper.MainActivity">
<item
android:id="@+id/to_clone"
android:orderInCategory="100"
android:title="@string/clone"
app:showAsAction="never" />
<item
android:id="@+id/to_setting"
android:orderInCategory="100"

View File

@ -3,5 +3,7 @@
<color name="colorPrimary">#1C8DD4</color>
<color name="colorPrimaryDark">#1B8DD4</color>
<color name="colorAccent">#63C2FA</color>
<color name="colorBlueGreyLight">#B0BBC5</color>
<color name="colorBlueGrey">#78909C</color>
<color name="colorBlueGreyDark">#546E7A</color>
</resources>

View File

@ -190,4 +190,10 @@
<string name="no_wifi_network">If the Wifi network is not connected, the one-click cloning function cannot be used.</string>
<string name="invalid_server_ip">Please enter a valid server IP address</string>
<string name="download_failed">Download Failed</string>
<string name="download_success">Download Success</string>
<string name="on_wireless_network">Currently on a wireless network</string>
<string name="on_mobile_network">Currently on a mobile network</string>
<string name="no_network">No network at present</string>
<string name="not_connected_wifi">Not connected WIFI</string>
<string name="failed_to_get_ip">Failed to get IP address</string>
</resources>

View File

@ -3,5 +3,7 @@
<color name="colorPrimary">#1C8DD4</color>
<color name="colorPrimaryDark">#1B8DD4</color>
<color name="colorAccent">#63C2FA</color>
<color name="colorBlueGreyLight">#B0BBC5</color>
<color name="colorBlueGrey">#78909C</color>
<color name="colorBlueGreyDark">#546E7A</color>
</resources>

View File

@ -174,7 +174,7 @@
<string name="post">POST</string>
<string name="get">GET</string>
<string name="local_ip">本机IP</string>
<string name="operating_instruction">操作说明:\n1.请保持新旧手机在同一个WiFi网络下且没有开启隔离\n2.旧手机直接点【发送】按钮获取到【服务端IP】\n3.新手机填写【服务端IP】后点【接收】按钮\n【注意】新手机接收后发送方、转发规则将完全被覆盖</string>
<string name="operating_instruction">操作说明:\n1.请保持新旧手机在同一个WiFi网络下且没有开启隔离\n2.旧手机直接点【发送】按钮获取到【服务端IP】\n3.新手机填写【服务端IP】后点【接收】按钮\n【注意】新手机接收后发送方、转发规则将完全被覆盖,清空历史记录</string>
<string name="send">发送</string>
<string name="stop">停止</string>
<string name="old_mobile_phone">我是旧手机</string>
@ -189,4 +189,10 @@
<string name="no_wifi_network">未接入Wifi网络不可使用一键克隆功能!</string>
<string name="invalid_server_ip">请输入服务端IP</string>
<string name="download_failed">下载文件失败</string>
<string name="download_success">下载成功</string>
<string name="on_wireless_network">当前处于无线网络</string>
<string name="on_mobile_network">当前处于移动网络</string>
<string name="no_network">当前没有网络</string>
<string name="not_connected_wifi">未连接Wifi</string>
<string name="failed_to_get_ip">获取IP失败</string>
</resources>

View File

@ -8,7 +8,7 @@ buildscript {
maven { url 'https://repo1.maven.org/maven2/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.2'
classpath 'com.android.tools.build:gradle:7.0.3'
classpath 'com.chenenyu:img-optimizer:1.2.0'
}
}

View File

@ -1,3 +1,3 @@
#Fri Jul 16 10:33:23 CST 2021
versionName=2.0.1
versionCode=27
versionName=2.1.0
versionCode=28