AndroidUtilCodeKTX (Ktx) has been open source for a month. So far 98 stars and 11 forks have been achieved on Github. During this period, she was listed on Github Trending Kotlin’s list, and also received praise and suggestions from some developers. After a month of slow updates, made some features THAT I wanted to add and fixed some issues that developers had reported back.

The latest version is 0.0.6:

implementation 'luyao. Util. KTX: AndroidUtilKTX: 0.0.6'
Copy the code

This version of the Change log:

* increaselogSwitch * adds textView.notempty () extension method * adds ShellExt, Run command line related functions * BaseVMFragment, BaseVMActivity add default exception handling callback * Android version check * Check whether barrier-free service is enabled * RecyclerView. ItemPadding * Send mail * File * BaseActivity/BaseFragment add CoroutineScope * automatically perceive the life cycle of KtxHandler KtxSpan Taiwan before and after listening to * * * Activity unified management application, Encapsulates the use of common spansCopy the code

Here is a brief overview of the major changes to version 0.0.6.

Series of null judgments

Thanks to the use of higher-order functions and lambda expressions, we can use our imagination to encapsulate some general logic. For example, for a nullable object, executing a specified operation in non-null time is equal to executing another operation in null time. Our code might be riddled with code with this structure:

if (obj == null) {... }else{... }Copy the code

Take a look at how this logic is written in Ktx:

obj.notNull({ ... }, {... })Copy the code

Well, a little more elegant (allow me to kid myself). The implementation is also simple, as follows:

fun <T>Any? .notNull(f: () -> T, t: () -> T): T {
    return if (this! =null) f() else t()
}
Copy the code

Return values are supported. Ignoring the less obvious optimization above, along the way, we can perform similar optimizations in slightly more complex scenarios. For example, if the text in a TextView is empty, you can define the following extension function:

fun TextView.notEmpty(f: TextView. () -> Unit, t: TextView.() -> Unit) {
    if (text.toString().isNotEmpty()) f() else t()
}
Copy the code

If you can think of more usage scenarios, feel free to hit the issue.

Executing shell commands

This is nothing special, but comes from a requirement in my Box project to read the Linux kernel version. In the end, it didn’t succeed, but it added a top-level function:

fun executeCmd(command: String): String {
    val process = Runtime.getRuntime().exec(command)

    val resultReader = BufferedReader(InputStreamReader(process.inputStream))
    val errorReader = BufferedReader(InputStreamReader(process.errorStream))

    val resultBuilder = StringBuilder()
    var resultLine: String? = resultReader.readLine()

    val errorBuilder = StringBuilder()
    var errorLine = errorReader.readLine()

    while(resultLine ! =null) {
        resultBuilder.append(resultLine)
        resultLine = resultReader.readLine()
    }

    while(errorLine ! =null) {
        errorBuilder.append(errorLine)
        errorLine = errorReader.readLine()
    }

    return "$resultBuilder\n$errorBuilder"
}
Copy the code

BaseActivity adds exception handling

This issue 7 from my WanAndroid project is mainly about exception handling for Kotlin Coroutine. There is exception handling as well, but the mException used to receive exceptions in BaseViewModel is private and cannot be retrieved directly. This version does general exception handling, which can be obtained directly from onError() callbacks in BaseVMActivity and BaseVMFragment. For details, see the exception handling demo in MvvmActivity.

Android Version Judgment

fun fromM(a) = fromSpecificVersion(Build.VERSION_CODES.M)
fun beforeM(a) = beforeSpecificVersion(Build.VERSION_CODES.M)
fun fromN(a) = fromSpecificVersion(Build.VERSION_CODES.N)
fun beforeN(a) = beforeSpecificVersion(Build.VERSION_CODES.N)
fun fromO(a) = fromSpecificVersion(Build.VERSION_CODES.O)
fun beforeO(a) = beforeSpecificVersion(Build.VERSION_CODES.O)
fun fromP(a) = fromSpecificVersion(Build.VERSION_CODES.P)
fun beforeP(a) = beforeSpecificVersion(Build.VERSION_CODES.P)
fun fromSpecificVersion(version: Int): Boolean = Build.VERSION.SDK_INT >= version
fun beforeSpecificVersion(version: Int): Boolean = Build.VERSION.SDK_INT < version
Copy the code

Check whether the barrier-free service is enabled

I met a small requirement in my work. I judged whether the service was started according to the name of barrier-free service.

fun Context.checkAccessbilityServiceEnabled(serviceName: String): Boolean {
    val settingValue =
        Settings.Secure.getString(applicationContext.contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
    return settingValue.notNull({
        var result = false
        val splitter = TextUtils.SimpleStringSplitter(':')
        while (splitter.hasNext()) {
            if (splitter.next().equals(serviceName, true)) {
                result = true
                break
            }
        }
        result
    }, { false})}Copy the code

RecyclerView.itemPadding

fun RecyclerView.itemPadding(top: Int, bottom: Int, left: Int = 0, right: Int = 0) {
    addItemDecoration(PaddingItemDecoration(top, bottom, left, right))
}
Copy the code

Add itemPadding quickly and easily, as shown below:

commonRecycleView.run {
    itemPadding(5.5.10.10)
    layoutManager = LinearLayoutManager(this@CommonListActivity)
    adapter = commonAdapter
}
Copy the code

The unit is dp and handles the dp2px logic internally.

Send E-mail

IntentExt added a function to send mail, supporting the most basic subject and text.

fun Context.sendEmail(email: String, subject: String? , text:String?). {
    Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:$email")).run {
        subject?.let { putExtra(Intent.EXTRA_SUBJECT, subject) }
        text?.let { putExtra(Intent.EXTRA_TEXT, text) }
        startActivity(this)}}Copy the code

Documents related to

The file-related parts were meant to be integrated in a big way, but the Kotlin standard library support for file manipulation is too good. I have a file utility class in AndroidUtilCode, and the standard library can do most of it. In order to more fully encapsulate the tool classes related to file management, I added a simple file management function to the Box project, which can be seen in the latest COMMIT.

Through this simple file manager module, the following extension properties and functions are mainly added on the basis of the standard library.

val File.canListFiles: Boolean: Indicates whether subfolders can be obtainedval File.totalSize: Long: Total size, including all subfoldersvalFile.formatSize: String: Total size of formatted files, including all subfoldersvalFile.mimeType: String: obtains the File mimeTypeListFiles (isRecursive: Boolean = false, filter: (File: File) -> Boolean)? = null): Array
      
        {} /** * [append] Whether to append * [text] to write * [charset] character encoding * / fun file.writetext (append: Boolean = false, text: String, charset: Charset = Charsets.UTF_8) fun File.writeBytes(append: Boolean = false, bytes: ByteArray) /** * [destFile] Destination file/folder * [overwrite] Whether to overwrite * [reserve] Whether to reserve the source file */
      
fun File.moveTo(destFile: File, overwrite: Boolean = true, reserve: Boolean = true)

/** * [destFolder] Target file/folder * [overwrite] Whether to overwrite * [func] Progress callback of a single file (from 0 to 100) */
fun File.moveToWithProgress(
    destFolder: File,
    overwrite: Boolean = true,
    reserve: Boolean = true,
    func: ((file: File, i: Int) -> Unit)? = null
)

fun File.rename(newName: String)
fun File.rename(newFile: File)
Copy the code

Most of the other common operations are already contained in the Kotlin Stdlib file, which you can read in/ Kotlin/IO/utils.kt.

CoroutineScope

Added in BaseActivity/BaseFragment CoroutineScope implementation, its subclasses can be directly through the launch {} using the main thread of coroutines scope, and in the onDestroy () will be automatically cancelled in the scope of startup coroutines. For those of you who have experience with coroutines, it should not be difficult to understand and the implementation is very simple.

abstract class BaseActivity : AppCompatActivity(), CoroutineScope byMainScope() { ... .override fun onDestroy(a) {
        super.onDestroy()
        cancel()
    }
}
Copy the code

It doesn’t matter if you haven’t used coroutines before, I’ll write a brief article on how to use coroutines properly on Android.

Automatic lifecycle aware KtxHandler

This is an auto-aware component lifecycle Handler that registers a LifecycleOwner and automatically cleans resources when the component onDestroy() occurs. This idea comes from the code I saw on the Internet. I really can’t remember the source. I will record the source in advance and mark it next time.

class KtxHandler(lifecycleOwner: LifecycleOwner, callback: Callback) : Handler(callback), LifecycleObserver {

    private val mLifecycleOwner: LifecycleOwner = lifecycleOwner

    init {
        lifecycleOwner.lifecycle.addObserver(this)}@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private fun onDestroy(a) {
        removeCallbacksAndMessages(null)
        mLifecycleOwner.lifecycle.removeObserver(this)}}Copy the code

For a simple use, see lifecycleActivity.kt.

Unified management of Activity/application monitoring

This comes from the requirements of the Issue area. The development requirements are nothing more than two functions: to close the specified Activity and to close all activities when exiting the application. These functions are defined in the singleton class KtxManager.

fun  finishActivity(clazz: Class< * >){
    for (activity in mActivityList)
        if (activity.javaClass == clazz)
            activity.finish()
}

fun finishAllActivity(a) {
    for (activity in mActivityList)
        activity.finish()
}
Copy the code

There are also examples of using both functions in lifecycleActivity.kt.

This is a clever use. To monitor the Activity by ActivityLifecycleCallbacks lifecycle, and ActivityLifecycleCallbacks commonly used way is to register in the Application. In Ktx, however, there is no Application class and no need for developers to write any code during integration. It automatically completes lifecycle registration listening. Some of you may have seen this trick in other places, if not, read ktx.kt, I believe you will understand.

By the way, the ProcessLifecycleOwner also listens to the application go to the foreground and background, and I’m sure you’ll see the corresponding Toast prompt.

KtxSpan

Finally, a Span utility class, to see what it looks like.

If you’re familiar with this diagram, you’re a loyal user of Blankji’s AndroidUtilCode. I’m not recreating the wheel here, nor am I copying AndroidUtilCode exactly. I have been a user of Material – Dialogs and like its API form very much, so I reconstructed KtxSpan by imitating its API structure.

KtxSpan().with(spanTv).show {
            text(
                "SpanUtils",
                foregroundColor = Color.YELLOW,
                backgroundColor = Color.LTGRAY,
                alignment = Layout.Alignment.ALIGN_CENTER
            )
            text("Foreground view", foregroundColor = Color.GREEN)
            text("Background color", backgroundColor = Color.LTGRAY)
            blockLine(spanTv.dp2px(6), true)
            text(High "line", lineHeight = 2 * spanTv.lineHeight, backgroundColor = Color.LTGRAY)
            text(
                "Test paragraph indent, first line, no other lines, no other lines.",
                first = (spanTv.textSize * 2).toInt(),
                rest = 0,
                backgroundColor = Color.GREEN
            )
            text(
                "Test the reference, the word after is to match two lines.",
                quoteColor = Color.GREEN,
                quoteStripeWidth = 10,
                quoteGapWidth = 10,
                backgroundColor = Color.LTGRAY
            )
            image(
                R.drawable.small,
                verticalAlignment = ImageSpan.ALIGN_BOTTOM,
                marginLeft = dp2px(30),
                marginRight = dp2px(40))}Copy the code

KtxSpan has only three methods, but that’s enough to cover most usage scenarios.

The first method, text(), represents most of the effects of text. Kotlin’s method arguments support default values, eliminating most method overloading. The text() method also has a large number of parameters. There are 31 parameters, except that text is required. By default, each text() method is a new line. You can also pass isNewLine: Boolean = false to make the next line non-newline.

The second method is image(), which represents the image display, with supporting properties such as alignment and left-right spacing.

The third method is blockLine(), which represents paragraph spacing. There are two parameters, one is the paragraph spacing value, and the other is whether to add spacing for all subsequent paragraphs.

For a specific use of KtxSpan, you can see the sample code in the project: KtxSpanActivity.

Last

That’s all for this update, AndroidUtilCodeKTX is a kid, you’re welcome!

This article first published wechat official account: BingxinshuaiTM, focusing on Java, Android original knowledge sharing, LeetCode problem solving.

AndroidUtilCodeKTX latest update information, scan code to follow me!