diff --git a/app/build.gradle b/app/build.gradle index 1cdd5bd..ad3f176 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,6 +75,7 @@ android { } dependencies { + implementation files('libs\\gmhelper.jar') // testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' @@ -110,7 +111,7 @@ dependencies { api project(path: ':locallib_quick_android:quick_android_custom_view') api project(path: ':locallib_quick_android:quick_android_easy_bar') api project(path: ':locallib_quick_android:quick_android_mvp') - api project(path: ':locallib_quick_android:quick_android_rx_permission') +// api project(path: ':locallib_quick_android:quick_android_rx_permission') // api project(path: ':locallib_quick_android/quick_android_qrcode') // api project(path: ':locallib_quick_android/quick_android_image_picker') // api project(path: ':locallib_quick_android/quick_android_file_picker') diff --git a/app/libs/gmhelper.jar b/app/libs/gmhelper.jar new file mode 100644 index 0000000..d436d08 Binary files /dev/null and b/app/libs/gmhelper.jar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd4f699..2ef816f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,6 +21,9 @@ + diff --git a/app/src/main/java/com/pkusz/min_vpn_client/GlobalSetting.java b/app/src/main/java/com/pkusz/min_vpn_client/GlobalSetting.java new file mode 100644 index 0000000..8dba707 --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/GlobalSetting.java @@ -0,0 +1,5 @@ +package com.pkusz.min_vpn_client; + +public class GlobalSetting { + public static boolean openBiometric = false; +} diff --git a/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivity.java b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivity.java new file mode 100644 index 0000000..de7b77a --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivity.java @@ -0,0 +1,253 @@ +package com.pkusz.min_vpn_client.activity.login; +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 19:45 2021/6/25 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ + +import android.animation.Animator; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.google.android.material.checkbox.MaterialCheckBox; +import com.pkusz.min_vpn_client.GlobalSetting; +import com.pkusz.min_vpn_client.R; +import com.pkusz.min_vpn_client.activity.base.MINVpnBaseActivity; +import com.pkusz.min_vpn_client.activity.welcome.WelcomeActivity; +import com.pkusz.min_vpn_client.model.MINVpnAPI; +import com.pkusz.min_vpn_client.model.request.LoginRequest; +import com.pkusz.min_vpn_client.utils.AccountValidatorRegexUtil; +import com.pkusz.min_vpn_client.utils.Base64Util; +import com.pkusz.min_vpn_client.utils.DigestUtilKt; +import com.pkusz.min_vpn_client.utils.KeyManager; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +import br.com.simplepass.loadingbutton.customViews.CircularProgressButton; +import cn.qjm253.min_vpn.extensions.ActivityExtensionsKt; +import cn.qjm253.quick_android_base.GlideApp; +import cn.qjm253.quick_android_base.extensions.ContextExtensionKt; +import cn.qjm253.quick_android_base.extensions.ViewExtensionKt; +import cn.qjm253.quick_android_base.util.DisplayUtils; +import cn.qjm253.quick_android_custom_view.other.ClearAbleEditText; +import cn.qjm253.quick_android_easy_bar.EasyBar; +import cn.qjm253.quick_android_easy_bar.EasyBarExtensionsKt; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +public class LoginActivity extends MINVpnBaseActivity + implements LoginActivityContract.View{ + // 常规变量区 + private String username=""; + private String password=""; + // 界面变量区 + private EasyBar easyBar; + private ImageView imageView_imgLogo; + private MaterialCheckBox savePasswordBox; + private TextView tvLoginWithSMS; + private TextView tvRegisterNow; + private TextView tvResetNow; + private ClearAbleEditText etUsername; + private CircularProgressButton btnLogin; + private ClearAbleEditText etPassword; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + // 初始化各个组件 + easyBar=findViewById(R.id.easyBar); + imageView_imgLogo=findViewById(R.id.imgLogo); + savePasswordBox=findViewById(R.id.savePasswordBox); + tvLoginWithSMS = findViewById(R.id.tvLoginWithSMS); + tvRegisterNow=findViewById(R.id.tvRegisterNow); + tvResetNow=findViewById(R.id.tvResetNow); + etUsername=findViewById(R.id.etUsername); + btnLogin=findViewById(R.id.btnLogin); + etPassword=findViewById(R.id.etPassword); + + initView(); + } + + private void initView(){ + // 初始化bar + EasyBarExtensionsKt.init(easyBar, EasyBar.Mode.ICON, String.valueOf(R.string.login), + (Function1) view -> { + System.out.println("假装跳转到扫码界面"); +// ContextExtensionKt.jumpTo(LoginActivity.this, QRcodeScanActivity.class); + return null; + }, + (Function1) view -> { + System.out.println("假装跳转到设置界面"); +// ContextExtensionKt.jumpTo(LoginActivity.this, SettingActivity.class); + return null; + }, + R.drawable.scan, R.drawable.setting, "", "", -1, + true, R.color.white); + + // glideapp + GlideApp.with(this) + .load(R.drawable.logo) + .circleCrop() + .into(imageView_imgLogo); + + // 判断是否已登录 + if(MINVpnAPI.INSTANCE.isLogin()){ + savePasswordBox.setChecked(true); + }else{ + savePasswordBox.setChecked(false); + } + + // 监听是否验证码登录 + tvLoginWithSMS.setOnClickListener(v -> { + System.out.println("假装跳转到验证码界面"); +// ContextExtensionKt.jumpTo(this, LoginSMSActivity.class); + }); + + // 监听注册按钮 + tvRegisterNow.setOnClickListener(v -> { + System.out.println("假装跳转到注册界面"); +// ContextExtensionKt.jumpTo(this, LoginSMSActivity.class); + }); + + // 监听重置密码按钮 + tvResetNow.setOnClickListener(v -> { + System.out.println("假装跳转到重置界面"); +// ContextExtensionKt.jumpTo(this, LoginSMSActivity.class); + }); + + // 显示账号 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + etUsername.setText(Base64Util.Base64PlusDecode(MINVpnAPI.INSTANCE.getUsername())); + }else{ + System.out.println("Base64Util.Base64PlusDecode error"); + } + + // 如果设置了密码保存,则显示密码 + if(savePasswordBox.isChecked()){ + etPassword.setText(MINVpnAPI.INSTANCE.getPassword()); + } + + // 登录按钮 + btnLogin.setBackgroundResource(R.drawable.button_shape_default); + btnLogin.setOnClickListener(v->{ + ContextExtensionKt.hideSoftKeyboard(this,btnLogin); + btnLogin.startAnimation(); + + this.username= Objects.requireNonNull(etUsername.getText()).toString(); + this.password= Objects.requireNonNull(etPassword.getText()).toString(); + + if(username.isEmpty()||password.isEmpty()){ + showWarning("请将信息填写完整"); + btnLogin.revertAnimation(); + return; + } + if(!AccountValidatorRegexUtil.INSTANCE.isPassword(password)){ + showWarning("请输入有效的密码,密码为8-16位,必须包含大小写字母和特殊符号"); + btnLogin.revertAnimation(); + return; + } + if(!AccountValidatorRegexUtil.INSTANCE.isUsername(username)){ + showWarning("请输入有效的用户名,用户名长度需要大于0小于20"); + btnLogin.revertAnimation(); + return; + } + + // 账号转base64 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + username=Base64Util.Base64PlusEncode(username); + }else{ + System.out.println("Base64Util.Base64PlusDecode error"); + } + + // 初始化keymanager + KeyManager.INSTANCE.initKeyChain("/"+username); + + // 登录server(密码转md5) + LoginRequest loginRequest=new LoginRequest(); + loginRequest.Prefix="/"+username; + loginRequest.Password= DigestUtilKt.encodeToMD5(password); + if(GlobalSetting.openBiometric){ + // 第一种模式,人脸识别登录,暂时废弃 +// mPresenter.downloadRegisterFacePicture(username,loginRequest,this); + }else{ + mPresenter.login(loginRequest); + } + }); + + // 监听键盘是否弹起 + ActivityExtensionsKt.listenKeyboardVisible(this,(isOpen)->{ + if(isOpen){ + ViewExtensionKt.animer(imageView_imgLogo, + ViewExtensionKt.scaleXAnimation(imageView_imgLogo,1f, 0.5f), + ViewExtensionKt.scaleYAnimation(imageView_imgLogo,1f, 0.5f), + ViewExtensionKt.transYAnimation(imageView_imgLogo, + 0f, -DisplayUtils.dp2px(this, 40)), + ViewExtensionKt.transYAnimation(etUsername, + 0f, -DisplayUtils.dp2px(this, 90)), + ViewExtensionKt.transYAnimation(etPassword, + 0f, -DisplayUtils.dp2px(this, 90)), + ViewExtensionKt.transYAnimation(btnLogin, + 0f, -DisplayUtils.dp2px(this, 90))); + }else{ + ViewExtensionKt.animer(imageView_imgLogo, + ViewExtensionKt.scaleXAnimation(imageView_imgLogo,0.5f, 1f), + ViewExtensionKt.scaleYAnimation(imageView_imgLogo,0.5f, 1f), + ViewExtensionKt.transYAnimation(imageView_imgLogo, + 0f, -DisplayUtils.dp2px(this, 40),0f), + ViewExtensionKt.transYAnimation(etUsername, + 0f, -DisplayUtils.dp2px(this, 90),0f), + ViewExtensionKt.transYAnimation(etPassword, + 0f, -DisplayUtils.dp2px(this, 90),0f), + ViewExtensionKt.transYAnimation(btnLogin, + 0f, -DisplayUtils.dp2px(this, 90),0f)); + } + return null; + }); + + + } + + + + + @NotNull + @Override + public LoginActivityPresenter onCreatePresenter() { + return new LoginActivityPresenter(this); + } + + @Override + public void loginSuccess() { + loginSuccess("登录成功"); + } + + @Override + public void loginSuccess(String successMsg) { + showSuccess(successMsg); + MINVpnAPI.INSTANCE.updateUsername(username); + if(savePasswordBox.isChecked()){ + MINVpnAPI.INSTANCE.updatePassword(password); + }else{ + MINVpnAPI.INSTANCE.updatePassword(""); + } + btnLogin.revertAnimation(); + System.out.println("假装跳转到了Main"); +// ContextExtensionKt.jumpTo(this, .class); + finish(); + } + + @Override + public void loginFailed(String errMsg) { + showWarning("登录失败: "+errMsg); + btnLogin.revertAnimation(); + } +} diff --git a/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityContract.java b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityContract.java new file mode 100644 index 0000000..2aa1302 --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityContract.java @@ -0,0 +1,39 @@ +package com.pkusz.min_vpn_client.activity.login; +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:28 2021/6/28 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ + +import android.content.Context; + +import com.pkusz.min_vpn_client.activity.base.MINVpnBaseView; +import com.pkusz.min_vpn_client.model.request.LoginRequest; + +import cn.qjm253.quick_android_mvp.base.mvp.BaseModel; +import cn.qjm253.quick_android_mvp.base.mvp.BasePresenter; + +public interface LoginActivityContract { + interface View extends MINVpnBaseView { + void loginSuccess(); + void loginSuccess(String successMsg); + void loginFailed(String errMsg); + } + + interface Model extends BaseModel { + void login(LoginRequest loginRequest); + void loginWithSafetyInfo(LoginRequest loginRequest, Context context); + void downloadRegisterFacePicture(String username,LoginRequest loginRequest, Context context); + } + + abstract class Presenter extends BasePresenter { + void loginSuccess(){}; + void loginSuccess(String successMsg){}; + void loginFailed(String errMsg){}; + void login(LoginRequest loginRequest){}; + void loginWithSafetyInfo(LoginRequest loginRequest, Context context){}; + void downloadRegisterFacePicture(String username,LoginRequest loginRequest, Context context){}; + } +} diff --git a/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityModel.java b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityModel.java new file mode 100644 index 0000000..3ece153 --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityModel.java @@ -0,0 +1,52 @@ +package com.pkusz.min_vpn_client.activity.login; +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:37 2021/6/28 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ + +import android.content.Context; + +import com.pkusz.min_vpn_client.model.Request_API; +import com.pkusz.min_vpn_client.model.request.LoginRequest; + +import common.LoggerHelper; + +public class LoginActivityModel implements LoginActivityContract.Model{ + private LoginActivityPresenter mPresenter; + + public LoginActivityModel(LoginActivityPresenter presenter){ + this.mPresenter=presenter; + } + + @Override + public void login(LoginRequest loginRequest) { +// 与MIS通信,获取返回信息 +// Request_API.INSTANCE.login() + // 假定成功登录,则执行下面语句 + boolean flag=true; + if(flag){ + mPresenter.loginSuccess(); + }else{ + mPresenter.loginFailed("测试登录失败..."); + } + } + + @Override + public void loginWithSafetyInfo(LoginRequest loginRequest, Context context) { + // 内含百度定位、mac地址等信息 + boolean flag=true; + if(flag){ + mPresenter.loginSuccess(); + }else{ + mPresenter.loginFailed("测试登录失败..."); + } + } + + @Override + public void downloadRegisterFacePicture(String username, LoginRequest loginRequest, Context context) { + LoggerHelper.warning("model.downloadRegisterFacePicture: 暂停人脸识别登录功能"); + } +} diff --git a/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityPresenter.java b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityPresenter.java new file mode 100644 index 0000000..ea4ff6a --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/activity/login/LoginActivityPresenter.java @@ -0,0 +1,49 @@ +package com.pkusz.min_vpn_client.activity.login; +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 16:35 2021/6/28 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ + +import android.content.Context; + +import com.pkusz.min_vpn_client.model.request.LoginRequest; + +public class LoginActivityPresenter extends LoginActivityContract.Presenter{ + public LoginActivityPresenter(LoginActivity loginActivity){ + mView=loginActivity; + mModel=new LoginActivityModel(this); + } + + @Override + void loginSuccess() { + mView.loginSuccess(); + } + + @Override + void loginSuccess(String successMsg) { + mView.loginSuccess(successMsg); + } + + @Override + void loginFailed(String errMsg) { + mView.loginFailed(errMsg); + } + + @Override + void login(LoginRequest loginRequest) { + mModel.login(loginRequest); + } + + @Override + void loginWithSafetyInfo(LoginRequest loginRequest, Context context) { + mModel.loginWithSafetyInfo(loginRequest, context); + } + + @Override + void downloadRegisterFacePicture(String username, LoginRequest loginRequest, Context context) { + mModel.downloadRegisterFacePicture(username, loginRequest, context); + } +} diff --git a/app/src/main/java/com/pkusz/min_vpn_client/activity/welcome/WelcomeActivity.java b/app/src/main/java/com/pkusz/min_vpn_client/activity/welcome/WelcomeActivity.java index 66205bc..29d3fba 100644 --- a/app/src/main/java/com/pkusz/min_vpn_client/activity/welcome/WelcomeActivity.java +++ b/app/src/main/java/com/pkusz/min_vpn_client/activity/welcome/WelcomeActivity.java @@ -8,28 +8,34 @@ package com.pkusz.min_vpn_client.activity.welcome; */ import android.os.Bundle; -import android.os.PersistableBundle; +import android.widget.ImageView; import androidx.annotation.Nullable; import com.pkusz.min_vpn_client.R; import com.pkusz.min_vpn_client.activity.base.MINVpnBaseActivity; -import com.pkusz.min_vpn_client.extensions.FileUtilsExtensions; +import com.pkusz.min_vpn_client.activity.login.LoginActivity; import org.jetbrains.annotations.NotNull; import cn.qjm253.min_vpn.extensions.FileUtilsExtensionsKt; +import cn.qjm253.quick_android_base.GlideApp; +import cn.qjm253.quick_android_base.QuickAndroid; +import cn.qjm253.quick_android_base.extensions.ContextExtensionKt; import cn.qjm253.quick_android_base.util.FileUtils; -import cn.qjm253.quick_android_mvp.base.mvp.BaseView; +import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; import io.reactivex.plugins.RxJavaPlugins; public class WelcomeActivity extends MINVpnBaseActivity implements WelcomeActivityContract.View{ + ImageView imageView_imgLogo; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_welcome); + imageView_imgLogo=findViewById(R.id.imgLogo); } @Override @@ -48,7 +54,45 @@ public class WelcomeActivity extends MINVpnBaseActivity { + Disposable disp = QuickAndroid.INSTANCE.createRxPermission(WelcomeActivity.this) + .request(premissions, "请求权限") + .subscribe(quickAndroidRxPermissionItem -> { + if (quickAndroidRxPermissionItem.getGranted()) { + System.out.println("开始跳转"); + ContextExtensionKt.jumpTo(WelcomeActivity.this,LoginActivity.class); + finish(); + } else { + System.out.println("无跳转权限"); + finish(); + } + }); + },600); + System.out.println("jump to login result: "+b); } @NotNull diff --git a/app/src/main/java/com/pkusz/min_vpn_client/model/MINVpnAPI.java b/app/src/main/java/com/pkusz/min_vpn_client/model/MINVpnAPI.java new file mode 100644 index 0000000..7a6cdee --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/model/MINVpnAPI.java @@ -0,0 +1,34 @@ +package com.pkusz.min_vpn_client.model; +/* + * @Author: Wang Feng + * @Description: + * @Version: 1.0.0 + * @Date: 17:23 2021/6/28 + * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 + */ + +public enum MINVpnAPI { + INSTANCE; + + private String username=""; + + public boolean isLogin(){ + return !username.equals(""); + } + + public String getUsername(){ + return username; + } + + public String getPassword(){ + return ""; + } + + public void updateUsername(String username){ + + } + + public void updatePassword(String password){ + + } +} diff --git a/app/src/main/java/com/pkusz/min_vpn_client/model/Request_API.java b/app/src/main/java/com/pkusz/min_vpn_client/model/Request_API.java index 0df942d..c8e8f80 100644 --- a/app/src/main/java/com/pkusz/min_vpn_client/model/Request_API.java +++ b/app/src/main/java/com/pkusz/min_vpn_client/model/Request_API.java @@ -24,9 +24,10 @@ import logicface.LogicFace; import logicface.LogicFaceException; import packet.Interest; -public class Request_API { +public enum Request_API { + INSTANCE; private boolean isConnected = false; - public LogicFace face = null; + private LogicFace face = null; private String globalPrefix="/mis"; private boolean tcpTestFinished=false; diff --git a/app/src/main/java/com/pkusz/min_vpn_client/model/request/LoginRequest.java b/app/src/main/java/com/pkusz/min_vpn_client/model/request/LoginRequest.java index 6d15f6d..44afaea 100644 --- a/app/src/main/java/com/pkusz/min_vpn_client/model/request/LoginRequest.java +++ b/app/src/main/java/com/pkusz/min_vpn_client/model/request/LoginRequest.java @@ -7,7 +7,7 @@ package com.pkusz.min_vpn_client.model.request; * @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 */ -public class LoginRequest { +public class LoginRequest extends BaseRequest{ // 继承属性 public String Type = BaseRequest.Type; public String Command = "Login"; diff --git a/app/src/main/java/com/pkusz/min_vpn_client/utils/AccountValidatorRegexUtil.kt b/app/src/main/java/com/pkusz/min_vpn_client/utils/AccountValidatorRegexUtil.kt new file mode 100644 index 0000000..a414821 --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/utils/AccountValidatorRegexUtil.kt @@ -0,0 +1,176 @@ +package com.pkusz.min_vpn_client.utils + +import java.util.regex.Pattern + +/** + * 账户相关属性验证工具 + */ +object AccountValidatorRegexUtil { + /** + * 正则表达式:验证用户名 + */ + val REGEX_USERNAME = "^[a-zA-Z]\\w{5,20}$" + + /** + * 正则表达式:验证密码 + */ + val REGEX_PASSWORD = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[\$@\$!%*.#?&])[A-Za-z\\d\$@\$!%*.#?&]{8,20}\$" + +/* val REGEX_PASSWORD1 = "^((?![a-z]+$)(?![A-Z]+$)(?!\\d+$)(?![!@#$%^&*._-]+$)(?![a-zA-Z\\d]+$)(?![a-zA-Z!@#$%^&*._-]+$)(?![\\d!@#$%^&*._-]+$)[a-zA-Z\\d!@#$%^&*._-]+){8,20}$" + val REGEX_PASSWORD2 = ".*[A-Z]+.*" + val REGEX_PASSWORD3 = ".*[a-z]+.*"*/ + /** + * 正则表达式:验证手机号 + */ + val REGEX_MOBILE = "^1[3-9]\\d{9}\$" + + /** + * 正则表达式:验证邮箱 + */ + val REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$" + + /** + * 正则表达式:验证汉字 + */ + val REGEX_CHINESE = "^[\u4e00-\u9fa5],{0,}$" + + /** + * 正则表达式:验证身份证 + */ + val REGEX_ID_CARD = "(^\\d{18}$)|(^\\d{15}$)" + + /** + * 正则表达式:验证URL + */ + val REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?" + + /** + * 正则表达式:验证IP地址 + */ + val REGEX_IP_ADDR = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\." + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$" + + /** + * 正则表达式:验证NDN前缀 + */ + val REGEX_NDN_PREFIX = "(/([a-z]|[A-Z]|[0-9]|-|_){1,40}){1,10}" + + + /** + * 正则表达式:验证NDN前缀 + */ + val REGEX_PORT = "([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])" + + + /** + * 校验用户名 + * + * @param username + * @return 校验通过返回true,否则返回false + */ + fun isUsername(username: String): Boolean { + //return Pattern.matches(REGEX_USERNAME, username) + return username.length > 0 && username.length < 20 + } + + /** + * 校验密码 + * + * @param password + * @return 校验通过返回true,否则返回false + */ + fun isPassword(password: String): Boolean { +/* return Pattern.matches(REGEX_PASSWORD1, password) && Pattern.matches(REGEX_PASSWORD2, password) + && Pattern.matches(REGEX_PASSWORD3, password)*/ + return Pattern.matches(REGEX_PASSWORD, password) + } + + /** + * 校验手机号 + * + * @param mobile + * @return 校验通过返回true,否则返回false + */ + fun isMobile(mobile: String): Boolean { + return Pattern.matches(REGEX_MOBILE, mobile) + } + + /** + * 校验邮箱 + * + * @param email + * @return 校验通过返回true,否则返回false + */ + fun isEmail(email: String): Boolean { + return Pattern.matches(REGEX_EMAIL, email) + } + + /** + * 校验汉字 + * + * @param chinese + * @return 校验通过返回true,否则返回false + */ + fun isChinese(chinese: String): Boolean { + return Pattern.matches(REGEX_CHINESE, chinese) + } + + /** + * 校验身份证 + * + * @param idCard + * @return 校验通过返回true,否则返回false + */ + fun isIDCard(idCard: String): Boolean { + return Pattern.matches(REGEX_ID_CARD, idCard) + } + + /** + * 校验URL + * + * @param url + * @return 校验通过返回true,否则返回false + */ + fun isUrl(url: String): Boolean { + return Pattern.matches(REGEX_URL, url) + } + + /** + * 校验IP地址 + * + * @param ipAddr + * @return + */ + fun isIPAddr(ipAddr: String): Boolean { + return Pattern.matches(REGEX_IP_ADDR, ipAddr) + } + + /** + * 校验NDN前缀 + * + * @param ndnPrefix + * @return + */ + fun isndnPrefix(ndnPrefix: String): Boolean { + return Pattern.matches(REGEX_NDN_PREFIX, ndnPrefix) + } + + /** + * 校验端口号 + * + * @param ndnPrefix + * @return + */ + fun isPort(port: String): Boolean { + return Pattern.matches(REGEX_PORT, port) + } + + /** + * 校验端口号 + * + * @param ndnPrefix + * @return + */ + fun isInviteCode(invitecode: String): Boolean { + return invitecode.length > 0 && invitecode.length < 20 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pkusz/min_vpn_client/utils/Base64Util.java b/app/src/main/java/com/pkusz/min_vpn_client/utils/Base64Util.java new file mode 100644 index 0000000..a13cc73 --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/utils/Base64Util.java @@ -0,0 +1,47 @@ +package com.pkusz.min_vpn_client.utils; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import java.io.UnsupportedEncodingException; +import java.util.Base64; + +public class Base64Util { + @RequiresApi(api = Build.VERSION_CODES.O) + public static String Base64PlusEncode(String srcStr){ + String dstStr = "" ; + try { + dstStr = Base64.getMimeEncoder().encodeToString(srcStr.getBytes("utf-8")); + } catch (UnsupportedEncodingException e) { + return dstStr ; + } + dstStr = dstStr.replaceAll("=","-").replaceAll("/","_") ; + return dstStr; + } + + @RequiresApi(api = Build.VERSION_CODES.O) + public static String Base64PlusDecode(String srcStr){ + srcStr = srcStr.replaceAll("_","/").replaceAll("-","=") ; + byte[] dstStrByte = null; + try { + dstStrByte = Base64.getMimeDecoder().decode(srcStr.getBytes("utf-8")); + } catch (UnsupportedEncodingException e) { + return new String(dstStrByte) ; + } + + return new String(dstStrByte); + } + + public static String BlocChainEncode(String srcStr) { + String dstStr = "" ; + try { + //dstStr = Base64.getMimeEncoder().encodeToString(srcStr.getBytes("utf-8")); + dstStr = android.util.Base64.encodeToString(srcStr.getBytes("utf-8"), + android.util.Base64.NO_WRAP | android.util.Base64.NO_PADDING | android.util.Base64.URL_SAFE); + } catch (UnsupportedEncodingException e) { + return dstStr ; + } + return dstStr; + } +} diff --git a/app/src/main/java/com/pkusz/min_vpn_client/utils/DigestUtil.kt b/app/src/main/java/com/pkusz/min_vpn_client/utils/DigestUtil.kt new file mode 100644 index 0000000..78946bd --- /dev/null +++ b/app/src/main/java/com/pkusz/min_vpn_client/utils/DigestUtil.kt @@ -0,0 +1,69 @@ +package com.pkusz.min_vpn_client.utils + +import org.zz.gmhelper.SM3Util +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException + +object DigestUtil { + fun md5(message: String) { + val digest = MessageDigest.getInstance("MD5") + digest.update(message.toByte()) + digest.digest() + } +} + +fun String.encodeToMD5(): String { + try { + //获取md5加密对象 + val instance: MessageDigest = MessageDigest.getInstance("MD5") + //对字符串加密,返回字节数组 + val digest: ByteArray = instance.digest(this.toByteArray()) + val sb = StringBuffer() + for (b in digest) { + //获取低八位有效值 + val i: Int = b.toInt() and 0xff + //将整数转化为16进制 + var hexString = Integer.toHexString(i) + if (hexString.length < 2) { + //如果是一位的话,补0 + hexString = "0$hexString" + } + sb.append(hexString) + } + return sb.toString() + + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } + + return "" +} + +fun String.encodeToSM3(): String { + try { +// //获取md5加密对象 +// val instance: MessageDigest = MessageDigest.getInstance("MD5") +// //对字符串加密,返回字节数组 + val digest:ByteArray = SM3Util.hash(this.toByteArray()) + //val digest: ByteArray = instance.digest(this.toByteArray()) + val sb = StringBuffer() + for (b in digest) { + //获取低八位有效值 + val i: Int = b.toInt() and 0xff + //将整数转化为16进制 + var hexString = Integer.toHexString(i) + if (hexString.length < 2) { + //如果是一位的话,补0 + hexString = "0$hexString" + } + sb.append(hexString) + } + return sb.toString() + + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } + + return "" +} + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index c92c561..0ba1020 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -5,7 +5,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="cn.qjm253.min_vpn.activity.login.LoginActivity"> + tools:context=".activity.login.LoginActivity"> diff --git a/locallib_quick_android/quick_android_base/build.gradle b/locallib_quick_android/quick_android_base/build.gradle index 82ca341..c2d7f18 100644 --- a/locallib_quick_android/quick_android_base/build.gradle +++ b/locallib_quick_android/quick_android_base/build.gradle @@ -36,6 +36,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "androidx.appcompat:appcompat:${appcompact_version}" +// implementation project(path: ':locallib_quick_android:quick_android_rx_permission') testImplementation "junit:junit:${junit_version}" implementation "androidx.constraintlayout:constraintlayout:${constraintlayout_version}" androidTestImplementation "androidx.test:runner:${test_runner_version}" diff --git a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/QuickAndroid.kt b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/QuickAndroid.kt index 860c45e..a7d0876 100644 --- a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/QuickAndroid.kt +++ b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/QuickAndroid.kt @@ -2,6 +2,8 @@ package cn.qjm253.quick_android_base import android.app.Application import android.content.Context +import androidx.fragment.app.FragmentActivity +import cn.qjm253.quick_android_rx_permission.QuickAndroidRxPermission import com.github.anzewei.parallaxbacklayout.ParallaxHelper import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.Logger @@ -38,4 +40,8 @@ object QuickAndroid { enableParallaxBack = true return this } + + fun createRxPermission(activity: FragmentActivity): QuickAndroidRxPermission { + return QuickAndroidRxPermission.create(activity) + } } \ No newline at end of file diff --git a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ContextExtension.kt b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ContextExtension.kt index 791a406..6c718a1 100644 --- a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ContextExtension.kt +++ b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ContextExtension.kt @@ -39,15 +39,22 @@ fun Context.jumpTo(cls: Class<*>, vararg flags: Int) { startActivity(intent) } +//fun jumpTo(vararg cls: Class<*>) { +// val intent = Intent(this, cls) +// flags.forEach { +// intent.addFlags(it) +// } +// startActivity(intent) +//} -fun Context.jumpTo(cls: Class<*>, intentParam: IntentParam? = null, vararg flags: Int) { - val intent = Intent(this, cls) - flags.forEach { - intent.addFlags(it) - } - intentParam?.applyParam(intent) - startActivity(intent) -} +//fun Context.jumpTo(cls: Class<*>, intentParam: IntentParam? = null, vararg flags: Int) { +// val intent = Intent(this, cls) +// flags.forEach { +// intent.addFlags(it) +// } +// intentParam?.applyParam(intent) +// startActivity(intent) +//} fun Activity.jumpForResult(cls: Class<*>, requestCode: Int = 0, intentParam: IntentParam? = null) { val intent = Intent(this, cls) @@ -146,4 +153,3 @@ fun Context.checkMyPermission(permission: String, granted: () -> Unit = {}, deny private fun autoGenericCode(code: String, num: Int): String { return String.format("%0" + num + "d", Integer.parseInt(code)) } - diff --git a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/FragmentManagerExtensions.kt b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/FragmentManagerExtensions.kt index 9e1ceb7..53ef01f 100644 --- a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/FragmentManagerExtensions.kt +++ b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/FragmentManagerExtensions.kt @@ -37,7 +37,7 @@ private fun getFragment(tag: String, clazz: Class, fragmentMana fragmentManager .beginTransaction() .add(fragment, tag) - .commitNow() + .commitNowAllowingStateLoss() } return fragment!! } diff --git a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ViewExtension.kt b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ViewExtension.kt index ea04d95..5aaba67 100644 --- a/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ViewExtension.kt +++ b/locallib_quick_android/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions/ViewExtension.kt @@ -108,7 +108,28 @@ fun View.animer(vararg animations: ObjectAnimator, duration: Long, listener: Ani if (listener != null) animatorSet.addListener(listener) animatorSet.start() +} +fun View.animer(vararg animations: ObjectAnimator, duration: Long) { + if (animations.isEmpty()) + return + val animatorSet = AnimatorSet() + val builder = animatorSet.setDuration(duration) + .play(animations[0]) + animations.filterIndexed { index, _ -> index != 0 } + .forEach { builder.with(it) } + animatorSet.start() +} + +fun View.animer(vararg animations: ObjectAnimator) { + if (animations.isEmpty()) + return + val animatorSet = AnimatorSet() + val builder = animatorSet.setDuration(200) + .play(animations[0]) + animations.filterIndexed { index, _ -> index != 0 } + .forEach { builder.with(it) } + animatorSet.start() } /** @@ -120,4 +141,4 @@ fun View.transRotateAlpha(transX: FloatArray = floatArrayOf(0f, 0f), transY: Flo listener: Animator.AnimatorListener? = null) { this.animer(transXAnimation(*transX), transYAnimation(*transY), rotationAnimation(*rotation), alphaAnimation(*alpha), duration = duration, listener = listener) -} \ No newline at end of file +} diff --git a/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermission.kt b/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermission.kt new file mode 100644 index 0000000..1369503 --- /dev/null +++ b/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermission.kt @@ -0,0 +1,69 @@ +package cn.qjm253.quick_android_rx_permission +// 迁移过来代码 +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import cn.qjm253.quick_android_base.QuickAndroid +import cn.qjm253.quick_android_base.extensions.getLazySingleton +import cn.qjm253.quick_android_base.util.RxSchedulersHelper +import io.reactivex.Observable + +/** + * @author SunnyQjm + * @date 19-7-8 上午9:03 + */ + +class QuickAndroidRxPermission { + companion object { + const val TAG = "QuickAndroidRxPermission" + + fun create(activity: FragmentActivity): QuickAndroidRxPermission { + return QuickAndroidRxPermission(activity) + } + + fun create(fragment: Fragment): QuickAndroidRxPermission { + return QuickAndroidRxPermission(fragment) + } + } + + private var mRxPermissionFragment: Lazy + private var requestCode = 0 + + constructor(activity: FragmentActivity) { + mRxPermissionFragment = activity.supportFragmentManager + .getLazySingleton(TAG, QuickAndroidRxPermissionFragment::class.java) + } + + constructor(fragment: Fragment) { + mRxPermissionFragment = fragment.childFragmentManager + .getLazySingleton(TAG, QuickAndroidRxPermissionFragment::class.java) + } + + + fun request(permissions: Array, @StringRes tip: Int): Observable { + requestCode++ + return mRxPermissionFragment.value.requestPermissions(permissions, tip, requestCode) + .compose(RxSchedulersHelper.io_main()) + } + + fun request(permissions: Array, tip: String = "请求权限"): Observable { + requestCode++ + return mRxPermissionFragment.value.requestPermissions(permissions, tip, requestCode) + .compose(RxSchedulersHelper.io_main()) + } + +// fun request(strings: Array): Any { +// +// } +} + +////////////////////////////////////////////////////////// +////////// QuickAndroid 扩展 +////////////////////////////////////////////////////////// +fun QuickAndroid.createRxPermission(activity: FragmentActivity): QuickAndroidRxPermission { + return QuickAndroidRxPermission.create(activity) +} + +fun QuickAndroid.createRxPermission(fragment: Fragment): QuickAndroidRxPermission { + return QuickAndroidRxPermission.create(fragment) +} \ No newline at end of file diff --git a/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermissionFragment.kt b/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermissionFragment.kt new file mode 100644 index 0000000..e4b5033 --- /dev/null +++ b/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermissionFragment.kt @@ -0,0 +1,77 @@ +package cn.qjm253.quick_android_rx_permission + +import android.os.Bundle +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment +import io.reactivex.Observable +import io.reactivex.ObservableEmitter +import pub.devrel.easypermissions.EasyPermissions + +/** + * @author SunnyQjm + * @date 19-7-8 上午9:13 + */ + +class QuickAndroidRxPermissionFragment : Fragment(), EasyPermissions.PermissionCallbacks { + + private val permissionsMap = + mutableMapOf>() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + retainInstance = true + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) + } + + /** + * 授权成功 + */ + override fun onPermissionsGranted(requestCode: Int, perms: MutableList) { + permissionsMap[requestCode]?.let { + it.onNext(QuickAndroidRxPermissionItem(true, requestCode, perms)) + it.onComplete() + permissionsMap.remove(requestCode) + } + } + + /** + * 授权失败 + */ + override fun onPermissionsDenied(requestCode: Int, perms: MutableList) { + permissionsMap[requestCode]?.let { + it.onNext(QuickAndroidRxPermissionItem(false, requestCode, perms)) + it.onComplete() + permissionsMap.remove(requestCode) + } + } + + + /** + * 请求权限 + */ + fun requestPermissions(permissions: Array, @StringRes tip: Int, requestCode: Int) + : Observable { + return requestPermissions(permissions, getString(tip), requestCode) + } + + + fun requestPermissions(permissions: Array, tip: String, requestCode: Int) + : Observable { + return Observable.create { emitter -> + permissionsMap[requestCode] = emitter + activity?.let { + if (EasyPermissions.hasPermissions(it, *permissions)) { + onPermissionsGranted(requestCode, permissions.toMutableList()) + } else { + EasyPermissions.requestPermissions(this, tip, requestCode, *permissions) + } + } + } + } + +} \ No newline at end of file diff --git a/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermissionItem.kt b/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermissionItem.kt new file mode 100644 index 0000000..eaa55e9 --- /dev/null +++ b/locallib_quick_android/quick_android_base/src/main/java/quick_android_rx_permission/QuickAndroidRxPermissionItem.kt @@ -0,0 +1,12 @@ +package cn.qjm253.quick_android_rx_permission + +/** + * @author SunnyQjm + * @date 19-7-8 上午9:34 + */ + +class QuickAndroidRxPermissionItem( + val granted: Boolean, + val requestCode: Int, + val perms: MutableList +) \ No newline at end of file diff --git a/locallib_quick_android/quick_android_rx_permission/src/main/java/cn/qjm253/quick_android_rx_permission/QuickAndroidRxPermission.kt b/locallib_quick_android/quick_android_rx_permission/src/main/java/cn/qjm253/quick_android_rx_permission/QuickAndroidRxPermission.kt index 139bbbd..c44f295 100644 --- a/locallib_quick_android/quick_android_rx_permission/src/main/java/cn/qjm253/quick_android_rx_permission/QuickAndroidRxPermission.kt +++ b/locallib_quick_android/quick_android_rx_permission/src/main/java/cn/qjm253/quick_android_rx_permission/QuickAndroidRxPermission.kt @@ -51,6 +51,10 @@ class QuickAndroidRxPermission { return mRxPermissionFragment.value.requestPermissions(permissions, tip, requestCode) .compose(RxSchedulersHelper.io_main()) } + +// fun request(strings: Array): Any { +// +// } } //////////////////////////////////////////////////////////