This chapter we start to refer to ATON to build a page——Actually in order to facilitate the use of UI resources for the Android version of Digging, we will basically refer to ATON in the future. Due to the layout code of Android is relatively large, we won’t post it one by one, and you can download the source code by yourself to see.
Splash Activity
Create activity_splash.xml in the directory res/layout with the following layout code.
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/color_ffffff" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_marginTop="220dp" android:src="@mipmap/splash_logo" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" android:contentDescription="TODO" /> <TextView android:id="@+id/tv_logo" style="@style/Text.000.12" android:layout_marginBottom="16dp" android:text="@string/copyright_resume" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
The ConstraintLayout is used here, and the SplashActivity class is created in the activity package with the following code:
class SplashActivity : AppCompatActivity() { private lateinit var bindding: ActivitySplashBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestWindowFeature(Window.FEATURE_NO_TITLE) val inflater = LayoutInflater.from(this) bindding = ActivitySplashBinding.inflate(inflater, null, false) setContentView(bindding.root) val handler = Handler(Looper.getMainLooper()) /** * 设置2s后跳转到页面 */ handler.postDelayed({ DXRouter.JumpAndFinish(this, OperateMenuActivity::class.java) }, 2000) } }
The command here is to jump to the specified page after 2s. Here a custom route jumping DXRouter is used, the code of the class is placed in the util package, which is as follows.
object DXRouter { /** * @param activity * @param activityClass */ fun JumpAndFinish(activity: Activity, activityClass: Class<out Activity?>?) { val intent = Intent(activity, activityClass) activity.startActivity(intent) activity.finish() } fun JumpTo(activity: Context, intent: Intent?) { activity.startActivity(intent) } fun JumpTo(activity: Context, activityClass: Class<out Activity?>?) { val intent = Intent(activity, activityClass) activity.startActivity(intent) } /** * @param activity * @param activityClass */ fun JumpAndWaitResult(activity: Activity, activityClass: Class<out Activity?>?, reqNO: Int) { val intent = Intent(activity, activityClass) JumpAndWaitResult(activity, reqNO, intent) } /** * @param activity * @param reqNO * @param intent */ fun JumpAndWaitResult(activity: Activity, reqNO: Int, intent: Intent?) { activity.startActivityForResult(intent, reqNO) } }
从代码可以看到该类只是对Activity的封装。最后把AndroidManifest.xml文件中的启动Activity修改为:SplashActivity 修改后的代码如下:
<application android:name="com.DiggingApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Digging" android:usesCleartextTraffic="true" tools:targetApi="m"> <activity android:name=".activity.SplashActivity" android:exported="true" android:launchMode="standard" android:screenOrientation="portrait" android:theme="@style/SplashTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
The effect:
Operate Menu Activity
Check activity_operate_menu.xml under res/layout for the specific layout code.
This page uses the ShadowContainer class of ATON. For convenience, I directly copied the code and put it under the com.platon.aton.widge package, then created the OperateMenuActivity class under the activity package with the following code:
class OperateMenuActivity : AppCompatActivity() { private lateinit var bindding: ActivityOperateMenuBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewUtil.SetWindow(window) ViewUtil.SetStatusBarColorToBlack(window) val inflater = LayoutInflater.from(this) bindding = ActivityOperateMenuBinding.inflate(inflater, null, false) setContentView(bindding.root) initEvent() } private fun initEvent() { /*** * 创建钱包 */ bindding.scCreateWallet.setOnClickListener { DXRouter.JumpTo(this, CreateWalletActivity::class.java) } /** * 导入钱包 */ bindding.scImportWallet.setOnClickListener { } } }
The logic of this page is currently just to do jumping with this page to achieve the creation of the wallet jump for the time being, the page effect is as follows.
You should note that the new Activity must be registered in AndroidManifest.xml.
Create Wallet Activity
For the specific layout code, see res/layout.
Create the file activity_create_wallet.xml
Create the CreateWalletActivity class with the following code.
import android.os.Bundle import android.text.Editable import android.text.TextUtils import android.text.TextWatcher import android.text.method.HideReturnsTransformationMethod import android.text.method.PasswordTransformationMethod import android.view.LayoutInflater import android.view.View import androidx.appcompat.app.AppCompatActivity import com.digquant.R import com.digquant.databinding.ActivityCreateWalletBinding import com.digquant.service.WalletManager import com.digquant.util.* class CreateWalletActivity : AppCompatActivity() { private lateinit var bindding: ActivityCreateWalletBinding /** * 钱包名是否符合规范 */ private var isEnableName = true /** * 密码是是否符合要求 */ private var isEnablePassword = true /** * 是否显示面 */ private var mShowPassword = false /** * 是否显示重复密码 */ private var mShowRepeatPassword = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewUtil.SetWindow(window) /** * 设置状态栏的颜色为黑色 */ ViewUtil.SetStatusBarColorToBlack(window) val inflater = LayoutInflater.from(this) bindding = ActivityCreateWalletBinding.inflate(inflater, null, false) setContentView(bindding.root) initUI() initEvent() } /** * 初始化UI */ private fun initUI() { enableCreate(false) showPassword() showRepeatPassword() } /** * 初始化事件 */ private fun initEvent() { /** * 返回Activity */ bindding.backBtn.setOnClickListener { this.finish(); } /** * 创建钱包 */ bindding.sbtnCreate.setOnClickListener { createWallet() } /** * 显示密码 */ bindding.ivPasswordEyes.setOnClickListener { showPassword() } /** * 显示重复密码 */ bindding.ivRepeatPasswordEyes.setOnClickListener { showRepeatPassword() } /** * 检测钱包名称 */ bindding.etName.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { /** * 判断钱包名是否符合规则,字符不能超过20个 */ val name = bindding.etName.text.toString().trim() when { TextUtils.isEmpty(name) -> { showNameError( ResourceUtil.GetString(R.string.validWalletNameEmptyTips), true ) } name.length > 20 -> { showNameError(ResourceUtil.GetString(R.string.validWalletNameTips), true) } else -> { showNameError("", false) } } } override fun afterTextChanged(s: Editable?) {} }) /** * 检测密码规范 */ bindding.etPassword.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val password = bindding.etPassword.text.toString().trim() val repeatPassword = bindding.etRepeatPassword.text.toString().trim() when { /** * 判断密码是否为空 */ TextUtils.isEmpty(password) -> { showPasswordError( ResourceUtil.GetString(R.string.validPasswordEmptyTips), true ) } /** * 判断密码强度 */ password.length < 6 -> { showPasswordError( ResourceUtil.GetString(R.string.validPasswordEmptyTips), true ) } /** * 判断重复密码是否相等 */ repeatPassword != password -> { showPasswordError(ResourceUtil.GetString(R.string.passwordTips), true) } else -> { if (password == repeatPassword) { showPasswordError("", false) } } } } override fun afterTextChanged(s: Editable?) {} }) /** * 监听重复密码 */ bindding.etRepeatPassword.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val password = bindding.etPassword.text.toString().trim() val repeatPassword = bindding.etRepeatPassword.text.toString().trim() when { /** * 判断密码是否为空 */ TextUtils.isEmpty(repeatPassword) -> { showPasswordError( ResourceUtil.GetString(R.string.validRepeatPasswordEmptyTips), true ) } /** * 判断重复密码是否相等 */ repeatPassword != password -> { showPasswordError(ResourceUtil.GetString(R.string.passwordTips), true) } else -> { if (password == repeatPassword) { showPasswordError("", false) } } } } override fun afterTextChanged(s: Editable?) { val password: String = bindding.etPassword.getText().toString().trim { it <= ' ' } checkPwdStrength(password) } }) } /** * 创建钱包 */ private fun createWallet() { val name = bindding.etName.text.toString().trim() val password = bindding.etPassword.text.toString().trim() val repeatPassword = bindding.etRepeatPassword.text.toString().trim() /** * 检测名字长度 */ if (name.length > 20) { showNameError(ResourceUtil.GetString(R.string.validWalletNameTips), true) return } /** * 检测密码 */ if (TextUtils.isEmpty(password)) { showPasswordError(ResourceUtil.GetString(R.string.validPasswordEmptyTips), true) return } /** * 检测重复密码 */ if (TextUtils.isEmpty(repeatPassword)) { showPasswordError(ResourceUtil.GetString(R.string.validRepeatPasswordEmptyTips), true) return } if (password != repeatPassword) { showPasswordError(ResourceUtil.GetString(R.string.passwordTips), true) return } /** * 去钱包管理类创建Session */ val isSucceed = WalletManager.BuildCreateWalletSession(name, password) if(!isSucceed) { ToastUtil.showLongToast(this, "创建钱包助记词失败") } DXRouter.JumpTo(this, BackupMnemonicPhraseActivity::class.java) } /** * 检测密码强度 */ private fun checkPwdStrength(password: String) { bindding.passwordStrength.visibility = if (TextUtils.isEmpty(password)) View.GONE else View.VISIBLE if (TextUtils.isEmpty(password)) { bindding.tvStrength.text = ResourceUtil.GetString(R.string.strength) bindding.vLine1.setBackgroundColor(ResourceUtil.GetColor(R.color.color_00000000)) bindding.vLine2.setBackgroundColor(ResourceUtil.GetColor(R.color.color_00000000)) bindding.vLine3.setBackgroundColor(ResourceUtil.GetColor(R.color.color_00000000)) bindding.vLine4.setBackgroundColor(ResourceUtil.GetColor(R.color.color_00000000)) return } when (CheckStrength.getPasswordLevelNew(password)) { CheckStrength.LEVEL.EASY -> { bindding.tvStrength.setTextColor(ResourceUtil.GetColor(R.color.color_f5302c)) bindding.tvStrength.setText(R.string.weak) bindding.vLine1.setBackgroundColor( ResourceUtil.GetColor(R.color.color_f5302c) ) bindding.vLine2.setBackgroundColor( ResourceUtil.GetColor(R.color.color_00000000) ) bindding.vLine3.setBackgroundColor( ResourceUtil.GetColor(R.color.color_00000000) ) bindding.vLine4.setBackgroundColor( ResourceUtil.GetColor(R.color.color_00000000) ) } CheckStrength.LEVEL.MIDIUM -> { bindding.tvStrength.setTextColor( ResourceUtil.GetColor(R.color.color_ff9000) ) bindding.tvStrength.setText(R.string.so_so) bindding.vLine1.setBackgroundColor( ResourceUtil.GetColor(R.color.color_ff9000) ) bindding.vLine2.setBackgroundColor( ResourceUtil.GetColor(R.color.color_ff9000) ) bindding.vLine3.setBackgroundColor( ResourceUtil.GetColor(R.color.color_00000000) ) bindding.vLine4.setBackgroundColor( ResourceUtil.GetColor(R.color.color_00000000) ) } CheckStrength.LEVEL.STRONG -> { bindding.tvStrength.setTextColor( ResourceUtil.GetColor(R.color.color_58b8ff) ) bindding.tvStrength.setText(R.string.good) bindding.vLine1.setBackgroundColor( ResourceUtil.GetColor(R.color.color_58b8ff) ) bindding.vLine2.setBackgroundColor( ResourceUtil.GetColor(R.color.color_58b8ff) ) bindding.vLine3.setBackgroundColor( ResourceUtil.GetColor(R.color.color_58b8ff) ) bindding.vLine4.setBackgroundColor( ResourceUtil.GetColor(R.color.color_00000000) ) } CheckStrength.LEVEL.VERY_STRONG, CheckStrength.LEVEL.EXTREMELY_STRONG -> { bindding.tvStrength.setTextColor( ResourceUtil.GetColor(R.color.color_19a20e) ) bindding.tvStrength.setText(R.string.strong) bindding.vLine1.setBackgroundColor( ResourceUtil.GetColor(R.color.color_19a20e) ) bindding.vLine2.setBackgroundColor( ResourceUtil.GetColor(R.color.color_19a20e) ) bindding.vLine3.setBackgroundColor( ResourceUtil.GetColor(R.color.color_19a20e) ) bindding.vLine4.setBackgroundColor( ResourceUtil.GetColor(R.color.color_19a20e) ) } else -> { } } } /** * 显示密码 */ private fun showPassword() { if (mShowPassword) { // 显示密码 bindding.etPassword.transformationMethod = HideReturnsTransformationMethod.getInstance() bindding.etPassword.setSelection(bindding.etPassword.text.toString().length) bindding.ivPasswordEyes.setImageResource(R.mipmap.icon_open_eyes) } else { // 隐藏密码 bindding.etPassword.transformationMethod = PasswordTransformationMethod.getInstance() bindding.etPassword.setSelection(bindding.etPassword.text.toString().length) bindding.ivPasswordEyes.setImageResource(R.mipmap.icon_close_eyes) } mShowPassword = !mShowPassword; } /** * 显示重复密码 */ private fun showRepeatPassword() { if (mShowRepeatPassword) { // 显示密码 bindding.etRepeatPassword.transformationMethod = HideReturnsTransformationMethod.getInstance() bindding.etRepeatPassword.setSelection(bindding.etRepeatPassword.text.toString().length) bindding.ivRepeatPasswordEyes.setImageResource(R.mipmap.icon_open_eyes) !mShowRepeatPassword } else { // 隐藏密码 bindding.etRepeatPassword.transformationMethod = PasswordTransformationMethod.getInstance() bindding.etRepeatPassword.setSelection(bindding.etRepeatPassword.text.toString().length) bindding.ivRepeatPasswordEyes.setImageResource(R.mipmap.icon_close_eyes) !mShowRepeatPassword } mShowRepeatPassword = !mShowRepeatPassword; } /** * 显示名字的错误信息 */ private fun showNameError(text: String?, isVisible: Boolean) { bindding.tvNameError.visibility = if (isVisible) View.VISIBLE else View.GONE bindding.tvNameError.text = text this.isEnableName = isVisible enableCreate(!isEnablePassword && !isEnableName) } /** * 显示面错误的信息 */ fun showPasswordError(text: String, isVisible: Boolean) { bindding.tvPasswordError.visibility = if (isVisible) View.VISIBLE else View.GONE bindding.tvPasswordError.text = text bindding.tvPasswordDesc.visibility = if (isVisible) View.GONE else View.VISIBLE isEnablePassword = isVisible enableCreate(!isEnablePassword && !isEnableName) } /** * */ private fun enableCreate(enabled: Boolean) { bindding.sbtnCreate.isEnabled = enabled } }
This class directly uses the detection logic of ATON, the main logic is:
1, detect whether the wallet name length is within 1~20 characters
2, detect whether the wallet password is greater than or equal to 6
In order to meet the conditions for creating a wallet, you need to go to the WalletManager to build a wallet creation callback, the wallet name and password temporarily stored inside, the page effect is as follows:
Backup Mnemonic Phrase Activity
For the specific layout code, see activity_backup_mnemonic_phrase.xml under res/layout
Create the BackupMnemonicPhraseActivity class under the activity package, the code of the class is as follows.
class BackupMnemonicPhraseActivity : AppCompatActivity() { private lateinit var bindding: ActivityBackupMnemonicPhraseBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewUtil.SetWindow(window) ViewUtil.SetStatusBarColorToBlack(window) val inflater = LayoutInflater.from(this) bindding = ActivityBackupMnemonicPhraseBinding.inflate(inflater, null, false) setContentView(bindding.root) initUI() initEvent() } private fun initUI() { val createWalletInfo = WalletManager.GetCreateWalletSession() val mnemonicWords = createWalletInfo?.mnemonicWords if (mnemonicWords != null) { // j将12个单词一次显示在页面 bindding.tvMnemonic1.text = mnemonicWords[0] bindding.tvMnemonic2.text = mnemonicWords[1] bindding.tvMnemonic3.text = mnemonicWords[2] bindding.tvMnemonic4.text = mnemonicWords[3] bindding.tvMnemonic5.text = mnemonicWords[4] bindding.tvMnemonic6.text = mnemonicWords[5] bindding.tvMnemonic7.text = mnemonicWords[6] bindding.tvMnemonic8.text = mnemonicWords[7] bindding.tvMnemonic9.text = mnemonicWords[8] bindding.tvMnemonic10.text = mnemonicWords[9] bindding.tvMnemonic11.text = mnemonicWords[10] bindding.tvMnemonic12.text = mnemonicWords[11] } } private fun initEvent() { bindding.backBtn.setOnClickListener { this.finish() } } }
The main logic of this class is to get the helper word of the currently created wallet talkback from the WalletManager class and display it on the page. The page effect is shown below:
Wallet Manager Analysis
This class is a pure static class used to manage all wallets and currently has three interfaces, BuildCreateWalletSession, GetCreateWalletSession, and ClearCreateWalletSession.
In order to generate helper words, you need to introduce the bitcoinj-core package in build.gradle. The logic for generating helper words is placed in two classes, JZWalletUtil and JZMnemonicUtil.
Well, that’s it for this chapter, next chapter we will start to learn how to generate the secret key.
The URL of github: https://github.com/DQTechnology/Platon_DevGuideProject
This article is reproduced from https://forum.latticex.foundation/t/topic/5958