diff --git a/.gitignore b/.gitignore
index 52a04b14..4018676e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ gradle.properties
/keystore/keystore.properties
/app/release
/keystore
+*.bak
diff --git a/README.md b/README.md
index ba954225..a699c260 100644
--- a/README.md
+++ b/README.md
@@ -205,7 +205,7 @@
--------
-## 更新记录:(PS.点击版本号下载对应的版本)
+## 更新记录:
+ [v1.0.0] 优化后第一版
+ [v1.1.0] 新增在线升级、缓存清理、加入QQ群功能
diff --git a/app/build.gradle b/app/build.gradle
index a880cc3e..4f2b40e3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c2d6d0ed..d9cb87be 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
package="com.idormy.sms.forwarder">
+
+
diff --git a/app/src/main/java/com/idormy/sms/forwarder/CloneActivity.java b/app/src/main/java/com/idormy/sms/forwarder/CloneActivity.java
new file mode 100644
index 00000000..49600d3b
--- /dev/null
+++ b/app/src/main/java/com/idormy/sms/forwarder/CloneActivity.java
@@ -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 _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);
+ }
+}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java b/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java
index abd4c590..8be2ef79 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java
+++ b/app/src/main/java/com/idormy/sms/forwarder/MainActivity.java
@@ -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;
diff --git a/app/src/main/java/com/idormy/sms/forwarder/utils/NetUtil.java b/app/src/main/java/com/idormy/sms/forwarder/utils/NetUtil.java
index ccce39d8..087bd49b 100644
--- a/app/src/main/java/com/idormy/sms/forwarder/utils/NetUtil.java
+++ b/app/src/main/java/com/idormy/sms/forwarder/utils/NetUtil.java
@@ -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));
+ }
}
diff --git a/app/src/main/java/com/idormy/sms/forwarder/view/IPEditText.java b/app/src/main/java/com/idormy/sms/forwarder/view/IPEditText.java
new file mode 100644
index 00000000..33b1504b
--- /dev/null
+++ b/app/src/main/java/com/idormy/sms/forwarder/view/IPEditText.java
@@ -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);
+ }
+}
diff --git a/app/src/main/res/drawable-mdpi/receive_btn.xml b/app/src/main/res/drawable-mdpi/receive_btn.xml
new file mode 100644
index 00000000..38ce05a9
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/receive_btn.xml
@@ -0,0 +1,16 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-mdpi/send_btn.xml b/app/src/main/res/drawable-mdpi/send_btn.xml
new file mode 100644
index 00000000..9956eb1b
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/send_btn.xml
@@ -0,0 +1,16 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_clone.xml b/app/src/main/res/layout/activity_clone.xml
new file mode 100644
index 00000000..792820aa
--- /dev/null
+++ b/app/src/main/res/layout/activity_clone.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/iptext.xml b/app/src/main/res/layout/iptext.xml
new file mode 100644
index 00000000..d26e5670
--- /dev/null
+++ b/app/src/main/res/layout/iptext.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
index ca7a66b2..6fb4e4d5 100644
--- a/app/src/main/res/menu/menu_main.xml
+++ b/app/src/main/res/menu/menu_main.xml
@@ -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">
+
- #1C8DD4
#1B8DD4
#63C2FA
-
+ #B0BBC5
+ #78909C
+ #546E7A
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index 7505cf8e..fb027260 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -190,4 +190,10 @@
If the Wifi network is not connected, the one-click cloning function cannot be used.
Please enter a valid server IP address
Download Failed
+ Download Success
+ Currently on a wireless network
+ Currently on a mobile network
+ No network at present
+ Not connected WIFI
+ Failed to get IP address
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 1fd28a17..aadc0402 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,5 +3,7 @@
#1C8DD4
#1B8DD4
#63C2FA
-
+ #B0BBC5
+ #78909C
+ #546E7A
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ba7109e7..76f48bf0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -174,7 +174,7 @@
POST
GET
本机IP:
- 操作说明:\n1.请保持新旧手机在同一个WiFi网络下,且没有开启隔离\n2.旧手机直接点【发送】按钮,获取到【服务端IP】\n3.新手机填写【服务端IP】后,点【接收】按钮\n【注意】新手机接收后,发送方、转发规则将完全被覆盖!
+ 操作说明:\n1.请保持新旧手机在同一个WiFi网络下,且没有开启隔离\n2.旧手机直接点【发送】按钮,获取到【服务端IP】\n3.新手机填写【服务端IP】后,点【接收】按钮\n【注意】新手机接收后,发送方、转发规则将完全被覆盖,清空历史记录!
发送
停止
我是旧手机
@@ -189,4 +189,10 @@
未接入Wifi网络,不可使用一键克隆功能!
请输入服务端IP
下载文件失败
+ 下载成功
+ 当前处于无线网络
+ 当前处于移动网络
+ 当前没有网络
+ 未连接Wifi
+ 获取IP失败
diff --git a/build.gradle b/build.gradle
index 5fb76649..4cb3b375 100644
--- a/build.gradle
+++ b/build.gradle
@@ -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'
}
}
diff --git a/version.properties b/version.properties
index 844d9004..27d4b5b3 100644
--- a/version.properties
+++ b/version.properties
@@ -1,3 +1,3 @@
#Fri Jul 16 10:33:23 CST 2021
-versionName=2.0.1
-versionCode=27
+versionName=2.1.0
+versionCode=28