Share Coding

Tutorials, Problems, Stuffs …

[Android]WebView scroll in a ScrollView / NestedScrollView

Layout:

<androidx.core.widget.NestedScrollView
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:background="@color/activity_background_color"
    android:fillViewport="false"
    android:orientation="vertical"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/toolbar">

<FrameLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:minHeight="350dp"
                android:layout_marginTop="20dp"
                android:layout_weight="1">

                <WebView
                    android:id="@+id/webview"
                    android:layout_width="match_parent"
                    android:layout_height="350dp"
                    android:nestedScrollingEnabled="true"
                    android:layout_marginHorizontal="20dp" />
            </FrameLayout>

</androidx.core.widget.NestedScrollView>
The following coding is the magic.

webView.setOnTouchListener { v, ev ->
    webView.requestDisallowInterceptTouchEvent(true)
    false
}

[Kotlin]Add function into class at independent class

The following example shows a use case of the additional function – EditText.focusAndShowKeyboard()

Since EditText do not have a single function to do that, so I add the following code at “EditTextUtil.kt”
Which is completely no change on the Class EditText.

fun AppCompatEditText.focusAndShowKeyboard() {
    /**
     * This is to be called when the window already has focus.
     */
    fun AppCompatEditText.showTheKeyboardNow() {
        if (isFocused) {
            post {
                // We still post the call, just in case we are being notified of the windows focus
                // but InputMethodManager didn't get properly setup yet.
                val imm =
                    context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
            }
        }
    }

    requestFocus()
    if (hasWindowFocus()) {
        // No need to wait for the window to get focus.
        showTheKeyboardNow()
    } else {
        // We need to wait until the window gets focus.
        viewTreeObserver.addOnWindowFocusChangeListener(
            object : ViewTreeObserver.OnWindowFocusChangeListener {
                override fun onWindowFocusChanged(hasFocus: Boolean) {
                    // This notification will arrive just before the InputMethodManager gets set up.
                    if (hasFocus) {
                        this@focusAndShowKeyboard.showTheKeyboardNow()
                        // It’s very important to remove this listener once we are done.
                        viewTreeObserver.removeOnWindowFocusChangeListener(this)
                    }
                }
            })
    }
}

And, without a class. We can create more tools for an old class or third party class.

Now you may enjoy:

val txtName = viewBinding.txtName
txtName.focusAndShowKeyboard()

After the above code you may focus to the EditText (txtName) and show keyboard.
The focusAndShowKeyboard function is for demonstration only, may be outed in the following days.

Android App Link / Universal Link Q&A Save a day 節省一天

App Link is the most easy maintenance and reliable method to redirect to other application from us.

How to use App Link?

  1. Setup AndroidManifest.xml
<!-- Google App Link -->
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />

    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />

    <!-- To Support Android 12, scheme should be in a data tag -->
    <data android:scheme="https" />
    <!-- Support Multi Build Flavor Path -->
    <data
        android:host="www.mydomain.com"
        android:path="@string/applink_google" />
</intent-filter>

PS: Different domain should separate by intent-filter.
- Multi domain for one activity supported.
- One domain for multi activity / apps supported.
- To Support Android 12, scheme should be in a data tag

2. Setup signature to different build flavour: UAT / Production (Suggested)

P.S.: See how to sign your app: https://developer.android.com/studio/publish/app-signing

3. Generate assets_link.json and put it into

https://domain.name/.well-known/assetlinks.json
Use App signing certificate SHA256 as production signature inside the assets file.

If you are not sure what signature has been installed, please use the following two command to check your device.

Check the app link status and signature of the app

adb shell pm get-app-links com.example.app

Forces reverify app link (After you make correction of signature in assets_link.json)

adb shell pm verify-app-links –re-verify com.example.app

When the system make App Link Verify?

In Android 12, system automatically verify app link, by download assets_link file after app installation. Or you may trigger it by the above command -> verify-app-links. If you cannot redirect by app link, may be your assets_link file has syntax error / place at wrong place / signature incorrect.

Tools – Google Statement List Generator and Tester

https://developers.google.com/digital-asset-links/tools/generator

4. Remind

The signature of the production APK is not equal with the play store installation, you should stick with the play store version.

5. Android 12 not working

Details about the internal site hosting of assetlinks.json:

  1. The certificates are not self-signed. They are valid and trusted.
  2. File is served with content-type application/json
  3. The file is served without any HTTP redirection
  4. Json file should save as utf-8 without BOM encoding.

5.1 Use Google Statement List Generator and Tester

After input your app link information, click Test statement.

Right click on the web page and click Element, open Network tab, on the bottom of the left side panel, you may found your network request of your assetlink.json.

You can found the error message here if it is not working.

5.2 Error: invalid argument: Could not parse statement list (not valid JSON): \357\273\277

If you get this error message, the section 6 will solve your problem.

6. How to save json file into UTF-8 without BOM

Open Sublime text at Mac, click file on menu bar, click Save with Encoding, click UTF-8.

Replace your new json file to the server. https://yourdomain/.well-known/assetlinks.json

Test again. Enjoy.

[Android] WebView scrolling not smooth, even it is very light weight

Scrolling is not smooth because of:

  • Drawing by software layer is too slow.
  • Images inside the website is too large.

Drawing by software layer

WebView Setting

webView.setLayerType(View.LAYER_TYPE_HARDWARE, null)

[Android]Device identification (Android ID, Fingerprints, DeviceID)

val fingerprinter = FingerprinterFactory
		.getInstance(applicationContext, Configuration(version = 3))

fingerprinter.getDeviceId { result ->
  val deviceId = result.mediaDrmId ?: result.gsfId ?: result.androidId // Use this ID
}

https://github.com/fingerprintjs/fingerprint-android
A android library can generate a device fingerprints without root permission.

This library provide threes modes in finger prints. Stable, Normal, Unique.
Or you may use method: getDeviceId() for special use.

The above example is suggested by the author. My project requirement is to identify if a device has redeemed the gift. After application uninstall and reinstalled, he is not allowed to redeem again.

No need to handle factory reset case. The description attached.

For the further findings at August-2021, there are no stable and unique deviceID can prevent being changed after factory reset.

After android 8.0, MAC address can be random by android setting, it is not stable.

Android ID can be changed after factory reset, or root device.