DQtech Column | Learn from DEX about PlatON Application Development – Android (III)

Author Dex_DQT

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:

DQtech Column | Learn from DEX about PlatON Application Development - Android (III)

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.

6A7E9CA8F19FE4C776A122F30264A8FE

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:

A9D3219C8A48C3A9D1D257F94D5E6C4D

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:

3E414C11C9F73F5F9C20A0145BB14EF7

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

Like (0)
Previous February 18, 2022 11:12
Next February 19, 2022 12:09

相关推荐

Leave a Reply

Please Login to Comment