<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ToddQ</title><description>Just some Android coding posts</description><link>https://blog.qtodd.com/</link><item><title>Enhancing app security with Kotlin inline functions</title><link>https://blog.qtodd.com/posts/kotlin-skill-1-enhancing-app-security-with-inline-functions/</link><guid isPermaLink="true">https://blog.qtodd.com/posts/kotlin-skill-1-enhancing-app-security-with-inline-functions/</guid><description>Kotlin Skill NO.1 - Use inline functions to protect your app against APK decompilation.</description><pubDate>Tue, 07 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;This is the first post about Kotlin skills, and we will discuss &lt;code&gt;inline&lt;/code&gt; functions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;🤔 What are inline functions ?&lt;/h2&gt;
&lt;p&gt;In case you are familiar with inline functions in Kotlin, feel free to skip this chapter.&lt;/p&gt;
&lt;p&gt;Inline functions are not exclusive to Kotlin. Simply put, when using an &lt;code&gt;inline&lt;/code&gt; function, the compiler will directly insert the function code into the code of the calling function, rather than creating a separate function object for the parameter and making a call to it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fun main() {
  check() { println(&quot;foo&quot;) }
}

inline fun check(action: () -&amp;gt; Unit) {
  println(&quot;begin&quot;)
  action()
  println(&quot;end&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above is a inlined function named &lt;code&gt;check&lt;/code&gt;, the compiler could emit the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fun main() {
  println(&quot;begin&quot;)
  println(&quot;foo&quot;)
  println(&quot;end&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason for doing these is to improve performance by avoiding the creation of objects by these &lt;a href=&quot;https://kotlinlang.org/docs/lambdas.html&quot;&gt;higher-order&lt;/a&gt; functions.&lt;/p&gt;
&lt;h2&gt;👍 Why can it enhance app security ?&lt;/h2&gt;
&lt;p&gt;Not inlining functions makes it easier for hackers to analyze their parameters and returns, potentially leading them to make assumptions about their purpose.&lt;/p&gt;
&lt;p&gt;By inlining certain sensitive functions, such as a paywall function, the compiler will copy it into each code of the caller, regardless of how many times it is called. This approach offers two advantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It makes it more challenging for hackers to identify the paywall function.&lt;/li&gt;
&lt;li&gt;It prevents hackers from easily editing the paywall function once to remove all paywalls.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is no guarantee of achieving 100% security, but it is indeed possible to increase the difficulty of hacking.&lt;/p&gt;
&lt;p&gt;🙏&lt;/p&gt;
</content:encoded></item><item><title>Automatically mirroring images for RTL layouts</title><link>https://blog.qtodd.com/posts/automatically-mirroring-images-for-rtl-layouts/</link><guid isPermaLink="true">https://blog.qtodd.com/posts/automatically-mirroring-images-for-rtl-layouts/</guid><description>Utilize LayoutInflater.Factory to hook Views</description><pubDate>Thu, 26 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;A simple but awesome trick from a &lt;a href=&quot;https://www.reddit.com/r/androiddev/comments/17gtrd2/comment/k6mti8h/?utm_source=share&amp;amp;utm_medium=web2x&amp;amp;context=3&quot;&gt;Reddit comment&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Define an integer resource &lt;code&gt;mirrorScale=1&lt;/code&gt; in &lt;code&gt;values&lt;/code&gt; folder, and &lt;code&gt;mirrorScale=-1&lt;/code&gt; in &lt;code&gt;values-ldrtl&lt;/code&gt; folder. Then you just set &lt;code&gt;android:scaleX=&quot;@integer/mirrorScale&quot;&lt;/code&gt; in layout xml files.&lt;/p&gt;
&lt;p&gt;The following section is my approach. If you want to learn more, please keep reading.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When Localizing Android for Arabic Language (RTL), you will notice that their reading direction is from right to left. Android provides built-in support for RTL layouts and text direction, which automatically mirrors text and layout elements.&lt;/p&gt;
&lt;p&gt;However, images do not undergo automatic mirroring. Achieving image mirroring for RTL languages typically involves two approaches, both requiring additional work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Manually Create Mirrored Images&lt;/strong&gt;
&amp;lt;br&amp;gt;Place a mirrored image with the same name in the corresponding &lt;strong&gt;ldrtl folder&lt;/strong&gt; within the drawable directory. For instance, put a mirrored image with the same filename in the &lt;code&gt;drawable-ldrtl-xhdpi&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use android:autoMirrored Attribute&lt;/strong&gt;
&amp;lt;br&amp;gt;Do not directly use images, create a drawable resource, for example:
&lt;code&gt;@drawable/icon&lt;/code&gt; refers to an image that needs to be mirrored&lt;pre&gt;&lt;code&gt; &amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
 &amp;lt;bitmap xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
     android:src=&quot;@drawable/icon&quot;
     android:autoMirrored=&quot;true&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
Then use this drawable resource instead.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Achieve by hooking&lt;/h2&gt;
&lt;p&gt;The 2nd approach mentioned above involves using the &lt;code&gt;android:autoMirrored=&quot;true&quot;&lt;/code&gt; attribute, which can automatically mirror drawables.&lt;/p&gt;
&lt;p&gt;Unfortunately, this attribute cannot be directly used in layout XML files. However, we can utilize Android&apos;s layout inflater mechanism to hook into the view tree generation process and modify ImageView&apos;s &lt;code&gt;src&lt;/code&gt; or View&apos;s &lt;code&gt;background&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;During the hooking process, we can use &lt;code&gt;drawable.setAutoMirrored(true)&lt;/code&gt; to achieve the same effect.&lt;/p&gt;
&lt;p&gt;Android provides hook interfaces for this purpose: &lt;code&gt;LayoutInflater.Factory&lt;/code&gt; and &lt;code&gt;LayoutInflater.Factory2&lt;/code&gt;, with the latter has one additional method. You can directly use &lt;code&gt;Factory2&lt;/code&gt;. In Activity &lt;code&gt;onCreate&lt;/code&gt; method, inject your custom &lt;code&gt;rtlFactory&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;val inflater = activity.layoutInflater
inflater.factory2 = rtlFactory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When generates views from XML, it triggers the &lt;code&gt;onCreateView()&lt;/code&gt; method within &lt;code&gt;rtlFactory&lt;/code&gt;. In this method, we have the ability to replace the generated ImageView objects with our specified custom View objects, such as &lt;code&gt;RtlImageView&lt;/code&gt; shown below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class RtlFactory : LayoutInflater.Factory2 {
    override fun onCreateView(
        parent: View?, name: String, context: Context, attrs: AttributeSet
    ): View? {
        if (name == &quot;ImageView) {
            return RtlImageVIew(context, attrs)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the &lt;code&gt;RtlImageView&lt;/code&gt;, you can access the image resource defined in the XML attributes, such as &lt;code&gt;android:src=&quot;@drawable/ic_logo&lt;/code&gt;, and enable automatic mirroring.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// RtlImageView
val drawable = AppCompatResources.getDrawable(view.context, srcResId)
drawable?.isAutoMirrored = true // Enable automatic mirroring
setImageDrawable(drawable)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, you can also add a custom attribute like &lt;code&gt;app:mirrorSrc=&quot;true&quot;&lt;/code&gt; to &lt;code&gt;RtlImageView&lt;/code&gt; to indicate whether automatic mirroring is required. The same principle applies to the View&apos;s background.&lt;/p&gt;
&lt;h2&gt;Use in XML layout&lt;/h2&gt;
&lt;p&gt;With the aforementioned setup, in your layout XML files, simply add the &lt;code&gt;app:mirrorSrc=&quot;true&quot;&lt;/code&gt; attribute to the ImageView whose image needs to be automatically mirrored.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ImageView
    android:layout_width=&quot;26dp&quot;
    android:layout_height=&quot;26dp&quot;
    app:mirrorSrc=&quot;true&quot;
    android:src=&quot;@drawable/icon&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have created a &lt;a href=&quot;https://github.com/qiantao94/AndroidAutoRtlImage/tree/master&quot;&gt;library&lt;/a&gt; contains a variety of Views for hooking all the Android native Views, including as TextView, Button, FrameLayout, and more.&lt;/p&gt;
</content:encoded></item><item><title>A trap in Android ViewGroup customization</title><link>https://blog.qtodd.com/posts/a-trap-in-android-viewgroup-customization/</link><guid isPermaLink="true">https://blog.qtodd.com/posts/a-trap-in-android-viewgroup-customization/</guid><description>A small code change can improve your custom ViewGroup&apos;s performance</description><pubDate>Fri, 20 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this post, we won&apos;t delve into the process of customizing a ViewGroup since there are many excellent guides available for that purpose. Instead, I&apos;ll highlight a trap in a crucial function within ViewGroup that is often overlooked by many developers.&lt;/p&gt;
&lt;p&gt;The crucial function in &lt;code&gt;ViewGroup&lt;/code&gt; is &lt;code&gt;shouldDelayChildPressedState()&lt;/code&gt;. Let&apos;s expoler it intended use.&lt;/p&gt;
&lt;h2&gt;The Function&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;public boolean shouldDelayChildPressedState() {
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In class &lt;code&gt;ViewGroup&lt;/code&gt;, the default implementation return &lt;code&gt;true&lt;/code&gt;. As it&apos;s named, the pressed state should be delayed for children or descendants of this ViewGroup. It is invoked by &lt;code&gt;isInScrollingContainer()&lt;/code&gt; in &lt;code&gt;ViewGroup&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public boolean isInScrollingContainer() {
    ViewParent p = getParent();
    while (p != null &amp;amp;&amp;amp; p instanceof ViewGroup) {
      if (((ViewGroup) p).shouldDelayChildPressedState()) {
        return true;
      }
      p = p.getParent();
    }
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function checks whether its parents are a scrolling container or not. It is invoked in the &lt;code&gt;View&lt;/code&gt;&apos;s touch event logic.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public boolean onTouchEvent(MotionEvent event) {
    ...
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            if (isInScrollingContainer()) { // &amp;lt;----
                postDelayed(mPendingCheckForTap,
                    ViewConfiguration.getTapTimeout());
            } else {
                setPressed(true, x, y);
            }
        break;
    }
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I mentioned before, when the view that has been touch is inside a scrolling container, it will delay the pressed feedback for a short period.&lt;/p&gt;
&lt;p&gt;The short period refers to &lt;code&gt;ViewConfiguration.getTapTimeout()&lt;/code&gt;, the value of which is &lt;strong&gt;100ms&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;The Trap&lt;/h2&gt;
&lt;p&gt;Regardless of when you touch the view, it will introduce a &lt;strong&gt;100ms&lt;/strong&gt; delay to verify the pressed state.&lt;/p&gt;
&lt;p&gt;OK, &lt;strong&gt;100ms&lt;/strong&gt; is a short duration for a single view, but it can become a significant factor when you prioritize app performance with a large number of views.&lt;/p&gt;
&lt;p&gt;So, we do not need this &lt;strong&gt;100ms&lt;/strong&gt; delay if the &lt;code&gt;ViewGroup&lt;/code&gt; is not scrollable, that means implementation of &lt;code&gt;shouldDelayChildPressedState&lt;/code&gt; should return &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The trap lies in the &lt;strong&gt;default implementation, which returns true&lt;/strong&gt;, and surprisingly, there are no official guides that mention it. It&apos;s easy to overlooked this.&lt;/p&gt;
&lt;p&gt;The implementations of ViewGroup&apos;s non-scrollable subclasses, such as &lt;code&gt;FrameLayout&lt;/code&gt; or &lt;code&gt;LinearLayout&lt;/code&gt;, return false.&lt;/p&gt;
&lt;p&gt;I prefer to &lt;strong&gt;use these subclasses&lt;/strong&gt; for customizing rather than using &lt;code&gt;ViewGroup&lt;/code&gt; directly to avoid these unexpected traps.&lt;/p&gt;
</content:encoded></item></channel></rss>