02 May 2020

ทำเว็ปให้รองรับในแอปแอนดรอยด์แบบเท่ๆด้วย Trusted Web Activity

Updated on

ในยุคที่เว็ปสามารถทำอะไรได้มากขึ้นกว่าเมื่อก่อน จึงทำให้นักพัฒนาบางคนตัดสินใจที่จะพัฒนาเว็ปเพื่อใช้งานในแอปแทนที่จะเขียนโค้ด Native (ไม่ว่าจะใช้แค่บางหน้าหรือใช้ทั้งแอปก็ตาม)

และเพื่อให้ผู้ใช้ได้สัมผัสประสบการณ์ในการในงานเว็ป (แบบไม่รู้ตัว) ที่ดีเหมือนกับ Native จึงทำให้เจ้าของบล็อกหยิบสิ่งที่เรียกว่า Trusted Web Activity หรือที่เรียกกันสั้นๆ TWA มาแนะนำให้ได้อ่านกันครับ

Trusted Web Activity คืออะไร?

เป็นความสามารถของ Chrome บนแอนดรอยด์ที่ทำให้นักพัฒนาสามารถเปิดเว็ปจากในแอปได้ ซึ่งจะช่วยให้นักพัฒนาสามารถทำแอปให้รองรับกับการเปิดเว็ปได้อย่างมีประสิทธิภาพมากขึ้น (เทียบเท่ากับการเปิดเว็ปบน Chrome) ไม่ว่าจะเป็นแอปที่ทำงานด้วยหน้าเว็ปทั้งหมด หรือแอปที่มีเว็ปอยู่ในบางหน้า

TWA ถูกเพิ่มเข้ามาในแอป Chrome บนแอนดรอยด์ตั้งแต่เวอร์ชัน 72 ขึ้นไป โดยมีเบื้องหลังในการทำงานแบบเดียวกับ Chrome Custom Tabs (เรียกสั้นๆว่า CCT)

เพิ่มเติม - สามารถอ่านเรื่องราวของ CCT ได้จากบทความ Chrome Custom Tabs ของดีที่จะช่วยให้การเปิดเว็ปในแอปเป็นเรื่องง่ายๆ

จุดเด่นของ Trusted Web Activity

ถึงแม้ว่าจะใช้ WebView และ CCT จะตอบโจทย์สำหรับการเปิดเว็ปในแอปได้ประมาณหนึ่ง แต่ทว่า TWA ก็มีจุดเด่นที่ทั้ง 2 อย่างทำไม่ได้ดังนี้

รองรับการซ่อน Title Bar สำหรับเว็ปที่ทำ Digital Asset Links

การใช้ CCT จะไม่สามารถซ่อน Title Bar ได้ เพราะว่าการเปิดหน้าเว็ปมีโอกาสโดนปลอมแปลงได้ จึงต้องแสดง Title Bar โดยมี URL แสดงอยู่ด้วยเสมอเพื่อให้ผู้ใช้สามารถเห็นและมั่นใจได้ว่าเป็นหน้าเว็ปของจริง


แต่กลับกันก็กลายเป็นข้อจำกัดสำหรับนักพัฒนาที่ต้องการเปิดเว็ปให้เต็มจอ ต้องการให้การใช้งานดูลื่นไหลไปในทิศทางเดียวกัน ไม่อยากให้สะดุดตาเพียงเพราะเห็น Title Bar ที่แตกต่างจากเดิมเพราะสลับไปมาระหว่างหน้าแอปกับหน้าเว็ป

โดย TWA จะรองรับการซ่อน Title Bar เพื่อแสดงเว็ปแบบเต็มจอได้ แต่มีเงื่อนไขว่าทั้งแอปและเว็ปจะต้องทำ Digital Asset Links เพื่อยืนยันว่าทั้งคู่นั้นถูกสร้างขึ้นมาจากนักพัฒนาคน/ทีมเดียวกัน


นั่นหมายความว่าถ้าเปิดเว็ปที่ไม่ได้ทำ Digital Asset Links (เว็ปที่ไม่ได้เป็นคนพัฒนาเอง) ใน TWA ก็จะยังคงแสดง Title Bar อยู่ดี


สำหรับการทำ Digital Asset Links จะเล่าให้ฟังในบทความหน้านะ (ถ้าเขียนเสร็จแล้วจะมาแปะลิ้งไว้ที่นี่ให้นะ)

ไม่ต้องกำหนดสี ก็รู้ว่าจะต้องแสดง Title Bar เป็นสีอะไร

ถ้านักพัฒนากำหนด Theme ด้วย AppCompat หรือ Material Design ไว้ในแอป จะทำให้ TWA ดึงสีจาก Primary มาใช้โดยอัตโนมัติ


ไม่ต้องเสียเวลาเขียนโค้ดเพื่อกำหนดสีให้กับ Title Bar เองแบบ CCT

จัดการเรื่อง Fallback ให้โดยอัตโนมัติ

ในกรณีที่ Chrome บนอุปกรณ์แอนดรอยด์ไม่รองรับ TWA จะทำการ Fallback ให้โดยอัตโนมัติ ซึ่งนักพัฒนาสามารถกำหนดเองได้ว่าจะให้ Fallback เป็น CCT หรือ WebView

รองรับ Splash Screen ในตัว

ในขณะที่ TWA กำลังโหลดข้อมูลของหน้าเว็ปนั้นๆอยู่ สามารถกำหนดให้แสดง Splash Screen จนกว่าหน้าเว็ปจะพร้อมแสดงผลได้ ซึ่งนักพัฒนาสามารถกำหนดภาพไอคอนและพื้นหลังสำหรับหน้า Splash Screen ได้ตามใจชอบ


สามารถแสดงผลแบบ Immersive Fullscreen ได้

เมื่อเรียกใช้งาน TWA ผ่านโค้ด (Programmatically) จะสามารถกำหนดให้แสดงผลแบบ Immersive Fullscreen ได้ ซึ่งเป็นการแสดงผลแบบเต็มจอโดยซ่อน Status Bar และ Navigation Bar


สามารถใช้ Notification ของแอนดรอยด์ได้

TWA มี Delegation Service สำหรับ Notification ให้ จึงทำให้เว็ปที่มี Notification สามารถจัดการกับ Notification ในระดับ Native ได้ เพื่อช่วยอำนวยความสะดวกสำหรับเว็ปที่ต้องการ Custom Notification เมื่อเปิดใช้งานในแอปบนแอนดรอยด์

ซึ่งในบทความนี้จะไม่ได้พูดถึงรายละเอียดของเรื่องนี้นะ

ข้อจำกัดของ Trusted Web Activity

ถึงแม้ว่า TWA นั้นจะมีความน่าสนใจเป็นอย่างมาก แต่เอาเข้าจริงก็ไม่ได้เหมาะกับทุกแอปที่มีการเปิดเว็ปภายในแอปเสมอไป ทั้งนี้ก็เพราะข้อจำกัดในการทำงานของ TWA นั่นเอง ซึ่งมีดังนี้

TWA แสดงผลเป็น Activity เท่านั้น

ก็ตามชื่อเรียกของมันแหละ จึงเหมาะกับกรณีที่แยกหน้าแอปและหน้าเว็ปออกจากกันอย่างสิ้นเชิงเท่านั้น

การรับส่งข้อมูลใน TWA จะต้องทำผ่าน URL เท่านั้น

แอปจะไม่สามารถเข้าถึงข้อมูลต่างๆของเว็ปอย่าง Cookies หรือ Local Storage ได้ ดังนั้นเว็ปที่เปิดใน TWA กับ Activity อื่นๆภายในแอปควรรับส่งข้อมูลกันผ่าน URL เช่น Query Parameter หรือ Intent URI (เว็ปที่เปิดบน Chrome ในแอนดรอยด์สามารถใช้ Intent ของแอนดรอยด์ได้)

เว็ปที่ไม่ได้ทำ Digital Asset Links ก็ซ่อน Title Bar ไม่ได้อยู่ดี

ทั้งนี้ก็เพราะว่าแอปและเว็ปควรจะเป็นนักพัฒนาคน/ทีมเดียวกัน ไม่ใช่แอปที่สร้างมาเพื่อเปิดหน้าเว็ปของนักพัฒนาคนอื่น

เว็ปที่มีการ Redirect ไปที่เว็ปอื่นๆ อย่างเช่น Payment Gateway Provider จะทำให้ Title Bar แสดงขึ้นมาเมื่อ Redirect ไปยังเว็ปเหล่านั้น แต่ Title Bar ก็จะซ่อนให้เมื่อ Redirect กลับมายังเว็ปที่ทำ Digital Asset Links ไว้

เงื่อนไขสำหรับเว็ปที่จะเปิดใน TWA

เพื่อประสบการณ์ที่ดีของผู้ใช้ จึงทำให้ทีม Google Play กำหนดเงื่อนไขไว้ว่าแอปที่ใช้งาน TWA ควรทดสอบใน Lighthouse ให้ผ่านตามเงื่อนไขดังนี้

• ต้องได้ PWA Badge ใน Lighthouse
• Performance Score ใน Lighthouse ต้องมากกว่า 80

Trusted Web Activity เหมาะกับการใช้งานแบบไหน?

• แยกการทำงานระหว่างส่วนที่เป็น Native กับส่วนที่เป็นเว็ปออกจากกัน คนละหน้า คนละฟีเจอร์ โดยมี API เป็นตัวกลางในการรับส่งข้อมูล

• เว็ปที่รองรับ PWA (Progressive Web Apps) และอยากทำเป็นแอป โดยไม่ต้องเขียนโค้ดบน Native ใหม่ทั้งหมด

ด้วยเหตุนี้ TWA จึงไม่ได้ตอบโจทย์ในการใช้งานไปซะทุกอย่าง ควรเลือกใช้ให้เหมาะสม โดยผู้ที่หลงเข้ามาอ่านสามารถดูตารางเปรียบเทียบข้อดีข้อเสียระหว่าง WebView, CCT และ TWA ได้จากภาพตารางข้างล่างนี้


ภาพตารางดังกล่าวเป็นภาพจากงาน Google I/O '19 ในหัวข้อที่ชื่อว่า Taking Chrome Full Screen with Trusted Web Activities

วิธีการใช้งาน Trusted Web Activity บนแอนดรอยด์

เริ่มจากเพิ่ม Dependency ของ TWA เข้าไปในโปรเจคก่อน

implementation 'com.google.androidbrowserhelper:androidbrowserhelper:1.1.0'

โดย Library ดังกล่าวจะมีชื่อเรียกว่า Android Browser Helper

สำหรับการเปิดเว็ปบน TWA นั้นจะมี 2 แบบด้วยกันคือการสั่งงานผ่านโค้ด (Programmatically และแบบกำหนดผ่าน Android Manifest ทั้งหมด ซึ่งจะมีจุดประสงค์ในการใช้งานแตกต่างกัน

ใช้งาน Trusted Web Activity ผ่านโค้ด (Programmatically)

การสั่งงานผ่านโค้ดจะเหมาะกับการใช้เว็ปเข้ามาเป็นส่วนหนึ่งในการทำงานของแอป โดยคำสั่งในรูปแบบที่ง่ายที่สุดจะเป็นแบบนี้

val uri = Uri.parse("https://akexorcist.com")
TwaLauncher(this).launch(uri)

แต่ถ้าอยากกำหนดค่าต่างๆให้กับ TWA ด้วย จะใช้ Builder แทน (เจ้าของบล็อกแนะนำให้ใช้แบบนี้ดีกว่า) โดยจะมีคำสั่งแบบนี้

val uri = Uri.parse("https://akexorcist.com/2020/04/android-app-bundle-part-1.html")
val builder = TrustedWebActivityIntentBuilder(uri)
    .setAdditionalTrustedOrigins(listOf("https://akexorcist.com"))
    .setDisplayMode(TrustedWebActivityDisplayMode.ImmersiveMode(true, 0))
    .setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary))
val strategy: SplashScreenStrategy? = null 
val completionCallback: Runnable? = null
TwaLauncher(this).launch(builder, strategy, completionCallback)

การใช้คำสั่ง launch(...) โดยกำหนดค่าด้วย TrustedWebActivityIntentBuilder จะมี SplashScreenStrategy กับ Runnable ให้นักพัฒนาสามารถกำหนดการทำงานของ Splash Screen (ระหว่างโหลดข้อมูลเว็ป) และ Runnable สำหรับตอนที่โหลดข้อมูลเสร็จแล้ว ซึ่งค่าทั้ง 2 ตัวนี้สามารถกำหนดเป็น Null ได้

การกำหนด Trusted Web

สำหรับเว็ปที่ทำ Digital Asset Links แล้ว จะต้องกำหนดไว้ใน TrustedWebActivityIntentBuilder ด้วยทุกครั้ง

val uri: Uri = ...
val builder = TrustedWebActivityIntentBuilder(uri)
    ...
    .setAdditionalTrustedOrigins(listOf(
        "https://akexorcist.com",
        "https://sleepingforless.com"
    ))

โดยจะเป็นการกำหนดค่าแบบ List ดังนั้นถ้ามีหลาย Domain ก็สามารถกำหนดไว้ในนี้ทั้งหมดได้เลย

การกำหนดสี Title/Status/Navigation Bar และ Display Mode

สำหรับการกำหนดสีจะกำหนดใน TrustedWebActivityIntentBuider แบบนี้ได้เลย

val context: Context = ...
val builder = TrustedWebActivityIntentBuilder(...)
    ...
    .setNavigationBarColor(ContextCompat.getColor(this, R.color.colorPrimary))
    .setToolbarColor(ContextCompat.getColor(this, R.color.toolbar))

โดยสีของ Status Bar จะเป็นสีเดียวกับ Title Bar (Toolbar) ไม่สามารถกำหนดแยกกันได้

และถ้าอยากจะแยกสีระหว่าง Light Mode และ Dark Mode ให้กำหนด Color Scheme แทน

val context: Context = ...
val lightColorParams: CustomTabColorSchemeParams = CustomTabColorSchemeParams.Builder()
    .setToolbarColor(ContextCompat.getColor(context, R.color.statusBar))
    .setNavigationBarColor(ContextCompat.getColor(context, R.color.navigationBar))
    .build()
val darkColorParams: CustomTabColorSchemeParams = CustomTabColorSchemeParams.Builder()
    .setToolbarColor(ContextCompat.getColor(context, R.color.statusBarDark))
    .setNavigationBarColor(ContextCompat.getColor(context, R.color.navigationBarDark))
    .build()
val builder = TrustedWebActivityIntentBuilder(...)
    ...
    .setColorScheme(CustomTabsIntent.COLOR_SCHEME_SYSTEM)
    .setColorSchemeParams(CustomTabsIntent.COLOR_SCHEME_LIGHT, lightColorParams)
    .setColorSchemeParams(CustomTabsIntent.COLOR_SCHEME_DARK, darkColorParams)

สำหรับ Display Mode จะมีให้เลือกระหว่าง Default กับ Immersive (ถ้าไม่ได้กำหนดจะใช้เป็น Default) โดยกำหนดด้วยคำสั่งแบบนี้

// Default Display Mode
val builder = TrustedWebActivityIntentBuilder(uri)
    .setDisplayMode(TrustedWebActivityDisplayMode.DefaultMode())

// Immersive Display Mode
val isSticky = true
val displayCutoutMode = 0
val builder = TrustedWebActivityIntentBuilder(uri)
    .setDisplayMode(TrustedWebActivityDisplayMode.ImmersiveMode(isSticky, displayCutouyMode))

การกำหนด Fallback

และในคำสั่ง launch(...) จะเห็นว่าผู้ที่หลงเข้ามาอ่านสามารถกำหนด Fallback ได้ด้วย

TwaLauncher.CCT_FALLBACK_STRATEGY
TwaLauncher.WEBVIEW_FALLBACK_STRATEGY

ถ้าไม่ได้กำหนด Fallback ไว้ จะเป็นการใช้ CCT Fallback โดยอัตโนมัติ

สำหรับการใช้ WebView Fallback จะต้องประกาศ Activity สำหรับ WebView Fallback และ Internet Permission ไว้ใน Android Manifest ด้วย

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ...>
        ...
        <activity
            android:name="com.google.androidbrowserhelper.trusted.WebViewFallbackActivity"
            android:configChanges="orientation|screenSize" />
    </application>
</manifest>

การกำหนด Splash Screen

โดยปกติแล้วการใช้งาน TWA ผ่านโค้ดจะไม่จำเป็นต้องใช้ Splash Screen ซักเท่าไร เพราะมันควรจะอยู่หน้าแรกสุดของแอปตั้งแต่ผู้ใช้เปิดขึ้นมาตอนแรก

แต่ถ้าจำเป็นต้องทำ ก็สามารถใช้ PwaWrapperSplashScreenStrategy เพื่อกำหนดการแสดง Splash Screen แบบนี้ได้เลย

val activity: Activity = this
val iconRedId: Int = R.drawable.ic_launcher_foreground
val backgroundColor: Int = ContextCompat.getColor(context, R.color.colorPrimary)
val scaleType: ImageView.ScaleType = ImageView.ScaleType.CENTER
val transformationMatrix: Matrix? = null
val fadeOutAnimationDuration: Int = 300
val fileProviderAuthority: String? = null
val strategy = PwaWrapperSplashScreenStrategy(
    activity,
    iconRedId,
    backgroundColor,
    scaleType,
    transformationMatrix,
    fadeOutAnimationDuration,
    fileProviderAuthority
)
val builder: TrustedWebActivityIntentBuilder = ...
TwaLauncher(this).launch(builder, strategy, null)

ในการแสดง Splash Screen จะเป็นการเพิ่ม View ของ Splash Screen เข้าไปใน Activity แบบ Programmatically จึงทำให้การลบ View ออกไปเมื่อโหลดข้อมูลเสร็จแล้วเป็นเรื่องยาก (เป็นอีกหนึ่งสาเหตุที่ไม่แนะนำให้ใช้ Splash Screen ในการใช้งาน TWA ผ่านโค้ด)

ใช้งาน Trusted Web Activity ผ่าน Android Manifest ทั้งหมด (Non-programmatically)

เหมาะสำหรับแอปที่ต้องการนำเว็ปของตัวเองที่มีอยู่แล้วมาแสดงในแอปแทนการเขียนโค้ด Native ทั้งหมด นั่นหมายความว่า วิธีนี้ไม่จำเป็นต้องแตะโค้ด Java/Kotlin บนแอนดรอยด์เลยซักนิด

โดยจะใช้ LauncherActivity ที่อยู่ใน TWA เพื่อกำหนดค่าต่างๆลงใน Android Manifest นั่นเอง ซึ่งมีรูปแบบคำสั่งเบื้องต้นแบบนี้

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ...>
        <activity
            android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
            android:label="@string/app_name">
            <meta-data
                android:name="android.support.customtabs.trusted.DEFAULT_URL"
                android:value="https://akexorcist.com" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <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" />

                <data
                    android:host="akexorcist.com"
                    android:scheme="https" />
            </intent-filter>
        </activity>
    </application>
</manifest>

ในการใช้ LauncherActivity ของ TWA สามารถกำหนด URL ของเว็ปที่ต้องการให้เปิดได้ โดยกำหนดไว้ใน Metadata ที่ชื่อว่า android.support.customtabs.trusted.DEFAULT_URL ที่อยู่ข้างใน Activity นั่นเอง

ที่ขาดไปไม่ได้ก็คือจะต้องกำหนด Intent Filter สำหรับแสดงไอคอนของแอปที่ Launcher ด้วย เพื่อให้ผู้ใช้กดไอคอนแอปจาก Launcher แล้วเปิดเว็ปใน TWA ได้เลย

รวมไปถึง Intent Filter สำหรับ Deep Linking เวลาที่ผู้ใช้กดเปิด URL ของเว็ปจากแอปอื่นๆภายในเครื่อง จะได้เด้งมาเปิดที่แอปทันที โดยมีการเปิดใช้งาน Auto Verify ด้วยเพื่อให้ TWA เช็ค Digital Asset Links ในเว็ปให้โดยอัตโนมัติ

และต้องประกาศ Internet Permission ไว้ในนี้ด้วยเสมอ

การกำหนด Trusted Web

สำหรับการกำหนดข้อมูลของเว็ปที่ทำ Digital Asset Links กับแอปไว้ จะเป็นข้อมูลที่เรียกว่า Asset Statements เพื่อเก็บข้อมูลของเว็ปในรูปของ JSON แบบนี้

[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "web",
      "site": "https://www.akexorcist.com/"
    }
  }
]

ถ้ามี Domain ที่ทำ Digital Asset Links ไว้มากกว่า 1 ตัว ก็กำหนดรวมกันไว้ในนี้ได้เลย

[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "web",
      "site": "https://www.akexorcist.com/"
    }
  },
  {
    "relation": [
      "delegate_permission/common.handle_all_urls"
    ],
    "target": {
      "namespace": "web",
      "site": "https://www.sleepingforless.com/"
    }
  }
]

สำหรับ Syntax ของ Asset Statements สามารถดูเพิ่มเติมได้ที่ Statement List Syntax [Google Digital Asset Links]

ในการกำหนด Asset Statements ใน Android Manifest จะกำหนดผ่าน Metadata ใน Application (ไม่ได้กำหนดไว้ใน Activity) ที่ชื่อว่า asset_statements ซึ่งข้อมูลของ Asset Statements ก็จะเก็บไว้ใน String Resource อีกทีหนึ่งเพื่อให้ง่ายต่อการอ่าน

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application
        ...>
        <activity
            android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
            android:label="@string/app_name">
            ...
        </activity>

        <meta-data
            android:name="asset_statements"
            android:resource="@string/asset_statements" />
    </application>
</manifest>

เนื่องจาก Asset Statements เก็บอยู่ในรูปของ String Resource ดังนั้นจะเก็บไว้ใน strings.xml ก็ได้ หรือจะสร้างไฟล์แยกออกมาเพื่อเก็บเฉพาะข้อมูลของ Asset Statements ก็ได้เช่นกัน

<!-- res/values/asset_statements.xml -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="asset_statements">
        [
            {
                \"relation\": [\"delegate_permission/common.handle_all_urls\"],
                \"target\": {
                    \"namespace\": \"web\",
                    \"site\": \"https://www.akexorcist.com/\"}
            }, {
                \"relation\": [\"delegate_permission/common.handle_all_urls\"],
                \"target\": {
                    \"namespace\": \"web\",
                    \"site\": \"https://www.sleepingforless.com/\"}
            }
        ]
    </string>
</resources>

การกำหนดสีของ Title/Status/Navigation Bar

สามารถกำหนดผ่าน Metadata แบบนี้ได้เลย

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application
        ...>
        <activity
            android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
            android:label="@string/app_name">
            ...            
            <meta-data
                android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR"
                android:resource="@color/statusBar" />

            <meta-data
                android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR_DARK"
                android:resource="@color/statusBarDark" />

            <meta-data
                android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR"
                android:resource="@color/navigationBar" />

            <meta-data
                android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR_DARK"
                android:resource="@color/navigationBarDark" />
        </activity>
    </application>
</manifest>

โดย Title Bar จะใช้สีเดียวกับ Status Bar ไม่สามารถกำหนดแยกกันได้

และจะเห็นว่าการกำหนดสีให้กับ TWA ผ่าน Metadata จะสามารถแยกสีระหว่าง Light Mode กับ Dark Mode ได้ด้วย แต่ถ้าผู้ที่หลงเข้ามาอ่านไม่ได้กำหนดสีสำหรับ Dark Mode ไว้ในนี้ TWA ก็จะใช้สีที่กำหนดไว้ใน Light Mode ให้แทน

การกำหนด Fallback

โดยปกติแล้ว ถ้าผู้ที่หลงเข้ามาอ่านไม่ได้กำหนด Fallback ไว้ จะใช้เป็น CCT Fallback โดยอัตโนมัติ แต่ถ้าต้องการเปลี่ยนไปใช้เป็น WebView Fallback ก็สามารถกำหนดผ่าน Metadata ที่ชื่อว่า android.support.customtabs.trusted.FALLBACK_STRATEGY แบบนี้ได้เลย

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application
        ...>
        <activity
            android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
            android:label="@string/app_name">
            ...            
            <meta-data android:name="android.support.customtabs.trusted.FALLBACK_STRATEGY"
                android:value="webview" />
        </activity>
    </application>
</manifest>

และอย่าลืมประกาศ Activity ของ WebView Fallback ไว้ใน Android Manifest ด้วยนะ

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application
        ...>
        ...
        <activity
            android:name="com.google.androidbrowserhelper.trusted.WebViewFallbackActivity"
            android:configChanges="orientation|screenSize" />
    </application>
</manifest>

การกำหนด Splash Screen

ในกรณีที่ต้องการแสดง Splash Screen ในระหว่างที่กำลังโหลดหน้าเว็ปอยู่ สามารถกำหนดผ่าน Metadata ใน Activity ได้ดังนี้

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    ...
    <application
        ...>
        <activity
            android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
            android:label="@string/app_name">
            ...            
            <meta-data android:name="android.support.customtabs.trusted.SPLASH_IMAGE_DRAWABLE"
                android:resource="@drawable/ic_splash"/>

            <meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_BACKGROUND_COLOR"
                android:resource="@color/splashBackground"/>

            <meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_FADE_OUT_DURATION"
                android:value="300"/>
        </activity>
    </application>
</manifest>

โดยผู้ที่หลงเข้ามาอ่านสามารถกำหนดได้ทั้งภาพไอคอน, สีพื้นหลัง และระยะเวลาของ Fade Out Animation เมื่อหน้าเว็ปโหลดเสร็จแล้ว

สรุป

Trusted Web Activity นั้นเหมาะกับเว็ปที่ต้องการแสดงผลในแอป ไม่ว่าจะเป็นการทำเว็ปที่มีอยู่แล้วมาแสดงผลบนแอปทั้งหมด หรือจะนำมาใช้งานเฉพาะบางหน้าหรือบางฟีเจอร์ ทั้งนี้ก็เพราะว่าการใช้ WebView นั้นเป็นอะไรที่เจ็บปวดมากๆสำหรับนักพัฒนาเว็ป จึงทำให้การใช้ TWA ที่ทำให้เว็ปสามารถทำงานได้เสมือนเปิดอยู่บน Chrome สามารถตอบโจทย์ได้ตรงจุดกว่า และรองรับ Digital Asset Links อีกด้วย 

จุดประสงค์หลักของ TWA นั้นคือการขยายพื้นที่ของ PWA ให้มากขึ้น มีความเป็นแอปมากขึ้น จากเดิมที่ต้องกด Add to Home Screen ใน Chrome เท่านั้น ก็สามารถทำเป็นแอปแอนดรอยด์และสามารถดาวน์โหลดจาก Google Play ได้ด้วย ซึ่งแน่นอนว่าประสิทธิภาพของ TWA นั้นดีกว่าการทำแอปที่ใช้วิธีเปิดบน WebView

ถ้าถามว่าเว็ปแบบไหนได้ผลประโยชน์มากที่สุดจากการใช้ TWA ก็คงต้องบอกว่าเว็ปที่รองรับ PWA นี่แหละ แต่การนำ TWA ไปใช้เป็นส่วนหนึ่งในแอปก็ถือว่าเป็นอีกวิธีที่น่าสนใจไม่น้อยเช่นกัน

แหล่งข้อมูลอ้างอิง

• Taking Chrome Full Screen with Trusted Web Activities (Google I/O ’19) [YouTube]
• Trusted Web Activity - Web [Google Developers]