"ไม่ต้องมีเวทมนตร์ ไม่ต้องไปหาแม่มด แค่คุณทำสิ่งที่โลกระลึกถึงตลอดกาล แค่นั้นคุณก็เป็นอมตะแล้ว"
มีอะไรใหม่ใน Android 7.1 Developer Preview ฉบับนักพัฒนา
20 Oct 2016 16:20   [15908 views]

หลังจากที่มีข่าวออกมาหลายสัปดาห์ว่ากำลังจะมี Android 7.1 ออกมานะ และแล้วล่าสุดมันก็ถูกปล่อยออกมาให้นักพัฒนาเล่นกันแล้วเมื่อคืนนี้ในรูปแบบของ Developer Preview จ้า มาดูกันว่าเจ้า Android 7.1 Developer Preview นี้มีอะไรใหม่บ้าง

ข้อมูลพื้นฐาน

ถึงจะเป็น Minor Version แต่ Android 7.1 เป็นการเปิด API Level ตัวใหม่รหัส 25 ถัดจากตัวเดิม Android 7.0 ซึ่งเป็น API Level 24 อย่างไรก็ตาม ทั้ง Android 7.0 และ Android 7.1 ยังคงถูกเรียกว่า Nougat อยู่

และถึงจะเพิ่งออก Developer Preview แต่ก็มีเครื่องที่เอามันไปใช้จริงแล้ว ... ไม่ใช่ใครที่ไหน ... Pixel นั่นเอง

ซึ่งแน่นอน พอ API Level เพิ่มเป็น 25 เจ้า Android SDK Build-tools ก็เลยแตกเวอร์ชันใหม่เวอร์ชัน 25 เพิ่มขึ้นมาด้วยเช่นกัน โดยเราสามารถโหลดเจ้า Android 7.1.1 (SDK Platform 25) และ Build-Tools 25 ได้จาก SDK Manager เช่นเคยครับ โหลดเลยๆ ของเล่นใหม่

เวลาจะทดสอบก็ปรับ buildToolsVersion เป็น 25.0.0 ส่วน compileSdkVersion และ targetSdkVersion ก็เปลี่ยนเป็น 25 ถือเป็นอันเรียบร้อย

android {    compileSdkVersion 25    buildToolsVersion "25.0.0"    defaultConfig {        ...        targetSdkVersion 25        ...    }    ...}

พร้อมกันนี้ก็มี Android Support Library v25.0.0 เพิ่มขึ้นมาให้ใช้ด้วยเช่นกัน เพื่อให้สามารถใช้งานร่วมกับ targetSdkVersion 25 ได้อย่างไม่มีปัญหา

    compile 'com.android.support:appcompat-v7:25.0.0'

ทั้งนี้ทั้งนั้น เพื่อให้สามารถใช้งาน Android 7.1 ได้อย่างสมบูรณ์แบบ เราแนะนำให้ทุกท่านอัปเดต Android Studio เป็นเวอร์ชันล่าสุด v2.2.2 ด้วย ซึ่งก็น่าจะอัปกันเป็นอยู่แล้วโนะะะะ

เมื่อเตรียมทุกอย่างเรียบร้อยแล้ว คราวนี้มาดูกันว่าเจ้า Android 7.1 นี้มีอะไรเพิ่มขึ้นมาจาก 7.0 บ้าง

App Shortcut

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

ซึ่งเอาจริงๆมันคือฟีเจอร์ที่ลอกมาจาก Quick Actions ของ iOS นั่นเอง

ที่ต่างคือ App Shortcut ของแอนดรอยด์ไม่ต้องพึ่ง Hardware พิเศษใดๆ สามารถใช้งานได้เลยบนหน้าจอใดๆด้วยการกดค้างที่ไอคอน

ในแง่ของการพัฒนาแล้ว เจ้าเมนูเหล่านี้สามารถระบุได้ทั้งแบบ Static (ฝังไปกับแอป) และ Dynamic (เพิ่ม-ลด-เปลี่ยนไปเรื่อยๆตามกาลเวลา) ดูจากภาพตัวอย่างด้านบนก็ได้ว่าจะมีปุ่ม + New Conversation อันนั้นถือเป็น Static เพราะจะเป็นแบบนั้นตลอดไป ส่วนอีกสามอันที่เหลือที่เป็นชื่อคน (Fran, K, Tristan) พวกนั้นเป็น Dynamic เพราะไม่ใช่ของที่ฝังมากับแอปแต่เริ่มต้นครับ

การระบุแบบ Static

ใครเขียนแอนดรอยด์มาก็น่าจะเดาได้ทันทีว่าต้องใส่ที่ไหน ... ถูกต้อง AndroidManifest.xml นั่นเองครับ โดยเชื่อมโยงกับไฟล์ xml ที่เอาไว้ Define ว่าเมนูไหนชื่ออะไร ทำอะไร หน้าตาเป็นยังไง


AndroidManifest.xml

สิ่งที่ต้องระบุเพิ่มเข้าไปคือ <meta-data> ที่ชื่อว่า android.app.shortcuts

<manifest xmlns:android="http://schemas.android.com/apk/res/android"             package="com.example.myapplication">  <application ... >    <activity android:name="Main">      <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.LAUNCHER" />      </intent-filter>      <meta-data android:name="android.app.shortcuts"                 android:resource="@xml/shortcuts" />    </activity>  </application></manifest>

ซึ่งมันจะเชื่อมกับไฟล์ xml ที่อยู่ใน Resource อีกทีนึง


res/xml/shortcuts.xml

ตรงนี้เราจะระบุ App Shortcut ด้วย tag <shortcuts> แล <shortcut> ที่เพิ่มมาใน API Level 25 ครับ

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">  <shortcut    android:shortcutId="compose"    android:enabled="true"    android:icon="@drawable/compose_icon"    android:shortcutShortLabel="@string/compose_shortcut_short_label1"    android:shortcutLongLabel="@string/compose_shortcut_long_label1"    android:shortcutDisabledMessage="@string/compose_disabled_message1">    <intent      android:action="android.intent.action.VIEW"      android:targetPackage="com.example.myapplication"      android:targetClass="com.example.myapplication.ComposeActivity" />    <!-- If your shortcut is associated with multiple intents, include them         here. The last intent in the list is what the user sees when they         launch this shortcut. -->    <categories android:name="android.shortcut.conversation" />  </shortcut>  <!-- Specify more shortcuts here. --></shortcuts>

การระบุแบบ Dynamic

เราจะระบุเมนูแบบ Dynamic ผ่าน ShortcutManager API ที่มีมาใน API Level 25 กันครับ โดยโค้ดหน้าตาจะเป็นประมาณนี้

ShortcutManager shortcutManager = getSystemService(ShortcutManager.class);ShortcutInfo shortcut = new ShortcutInfo.Builder(this, "id1")    .setShortLabel("Web site")    .setLongLabel("Open the web site")    .setIcon(Icon.createWithResource(context, R.drawable.icon_website))    .setIntent(new Intent(Intent.ACTION_VIEW,                   Uri.parse("https://www.mysite.example.com/")))    .build();shortcutManager.setDynamicShortcuts(Arrays.asList(shortcut));

ซึ่งจะเอาโค้ดไปไว้ที่ไหนก็ได้แล้วแต่ศรัทธาและความเหมาะสมครับ

การ Handle App Shortcut Event

อีกเช่นกัน ถ้าเป็น Android Developer พอเห็นภาพแรกสุดด้านบนโน้นก็จะเดาออกทันทีว่าถ้าผู้ใช้กดเมนูต่างๆใน App Shortcut ตัวระบบปฏิบัติการณ์จะส่ง Event มาทางแอปเราผ่านอะไร

ถูกต้องครับ ... Intent นั่นเอง ซึ่งดูจากโค้ดด้านบนก็จะเห็นว่าไม่ว่าจะเป็นแบบ Static หรือ Dynamic ล้วนมี Intent ถูกตั้งไว้ นั่นแหละครับคือสิ่งที่เราต้อง Handle กัน ซึ่งการ Handle Intent ตรงนี้ก็ไม่มีอะไรต่างจากเดิมเลย (ตรวจจาก getIntent() ตรง onCreate) ดังนั้นตรงนี้ไม่ต้องพูดถึงเนอะะะ


ตอนนี้ก็น่าจะเห็นภาพแล้วว่า App Shortcut ทำงานยังไง เขียนโค้ดกันยังไง ทั้งนี้ทั้งนั้น App Shortcut จะใช้ได้เฉพาะบน Android 7.1 เท่านั้น ยังไม่มีท่า Backward Compatible ใดๆครับ

สำหรับนักพัฒนาที่ต้องการข้อมูลเพิ่มเติมสามารถเปิดดูได้จาก App Shortcuts เลยจ้า และถ้าต้องการดูตัวอย่างโค้ดก็สามารถกดเมนู File -> Import Samples จาก Android Studio และหา AppShortcuts ได้เลยครับ

Image Keyboard Support

ฟีเจอร์นี้มาเหนือมาก เมื่อยุคนี้คีย์บอร์ดส่งแค่ข้อความกับ Emoji ยังไม่สะใจพอ นี่เลย ใช้คีย์บอร์ดส่งรูปกับฟีเจอร์นี้ Image Keyboard

อาจจะมีคนนึกภาพไม่ออกว่า เอ๊ะ มันมีประโยชน์ยังไง

Use Case ง่ายๆที่ทุกคนน่าจะเข้าใจได้ไม่ยากคือการส่งภาพเคลื่อนไหว GIF ตลกๆให้เพื่อนผ่านทาง Messenger นั่นเอง ไม่ต้องไปควานหารูปอะไรให้ยุ่งยากละเพราะสามารถส่งจากรูปที่มีให้ในคีย์บอร์ดได้เลย

โดยฟังก์ชัน Image Keyboard นี้จะประกอบด้วยการทำงานสองส่วนด้วยกัน คือ

1) EditText ต้องบอกว่าตัวเองสามารถรับ Content Type แบบไหนได้บ้าง โดยระบุเป็น MIME Type ไปในโค้ด หากไม่ระบุ MIME Type ไว้ EditText นั้นๆก็จะไม่สามารถใช้งานร่วมกับ Image Keyboard ได้

2) ต้องใส่โค้ดในตัวคีย์บอร์ด (IME) เพื่อเพิ่มความสามารถในการส่งรูปด้วย

ไดอะแกรมเป็นตามนี้ ส่วนบนเป็นของ EditText ส่วนล่างเป็นของ IME

ส่วนนี่เป็นโค้ดจ่ะ

การเพิ่มซัพพอร์ต Image Keyboard ให้ EditText

อันนี้เป็นโค้ดตัวอย่างของฝั่ง EditText ตามที่บอกคือต้องระบุ MIME Type ที่ซัพพอร์ตไว้ในโค้ดผ่านคำสั่งแบบนี้ (ตัวอย่างนี้บอกว่ารับไฟล์ png)

EditText editText = new EditText(this) {    @Override    public InputConnection onCreateInputConnection(EditorInfo editorInfo) {        final InputConnection ic = super.onCreateInputConnection(editorInfo);        EditorInfoCompat.setContentMimeTypes(editorInfo,                new String [] {"image/png"});        final InputConnectionCompat.OnCommitContentListener callback =            new InputConnectionCompat.OnCommitContentListener() {                @Override                public boolean onCommitContent(InputContentInfoCompat inputContentInfo,                        int flags, Bundle opts) {                    // read and display inputContentInfo asynchronously                    if (BuildCompat.isAtLeastNMR1() && (flags &                        InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {                        try {                            inputContentInfo.requestPermission();                        }                        catch (Exception e) {                            return false; // return false if failed                        }                    }                    // read and display inputContentInfo asynchronously.                    // call inputContentInfo.releasePermission() as needed.                    return true;  // return true if succeeded                }            };        return InputConnectionCompat.createWrapper(ic, editorInfo, callback);    }};

เมื่อ Content ถูกส่งมาจาก Image Keyboard เราก็ต้อง Handle เองว่าจะทำงานกับรูปนั้นๆอย่างไรในส่วน onCommitContent ครับ

สำหรับ EditText ที่ไม่ได้ต้องการให้ใช้งาน Image Keyboard ได้ ให้งดเว้นการเรียก setContentMimeTypes() เพื่อหลีกเลี่ยงปัญหาที่อาจเกิดขึ้นครับ

โค้ดตัวอย่างสามารถเปิดดูจาก Samples ที่ชื่อว่า CommitContentSampleApp จ้า

การเพิ่มซัพพอร์ต Image Keyboard ให้ IME

ฝั่ง IME ก็เริ่มต้นจากการเช็ค MIME Type ว่าตัว EditText ที่ร้องขอมานั้นมีการขอใช้ MIME Type ที่ตรงกับที่คีย์บอร์ดมีรึเปล่า

@Overridepublic void onStartInputView(EditorInfo info, boolean restarting) {    String[] mimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);    boolean gifSupported = false;    for (String mimeType : mimeTypes) {        if (ClipDescription.compareMimeTypes(mimeType, "image/gif")) {            gifSupported = true;        }    }    if (gifSupported) {        // the target editor supports GIFs. enable corresponding content    } else {        // the target editor does not support GIFs. disable corresponding content    }}

ส่วนนี่เป็นโค้ดตอนส่งรูปจาก IME ไป EditText ครับ

/** * Commits a GIF image * * @param contentUri Content URI of the GIF image to be sent * @param imageDescription Description of the GIF image to be sent */public static void commitGifImage(Uri contentUri, String imageDescription) {    InputContentInfoCompat inputContentInfo = new InputContentInfoCompat(            contentUri,            new ClipDescription(imageDescription, new String[]{"image/gif"}));    InputConnection inputConnection = getCurrentInputConnection();    EditorInfo editorInfo = getCurrentInputEditorInfo();    Int flags = 0;    If (android.os.Build.VERSION.SDK_INT >= 25) {        flags |= InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION;    }    InputConnectionCompat.commitContent(            inputConnection, editorInfo, inputContentInfo, flags, opts);}

ทุกอย่างก็ค่อนข้างตรงไปตรงมา อาจจะมีวุ่นเรื่อง Runtime Permission นิดหน่อย แต่ก็ทำตามด้านบนได้เลย ไม่มีปัญหา

ตัวอย่างโค้ดของ Image Keyboard บน IME ก็มีเช่นกัน เปิดใน Samples แล้วหา CommitContentSampleIME ได้เลยครับ


ข้อมูลเพิ่มเติมสำหรับนักพัฒนา อ่านที่ Image Keyboard ได้เลยคร้าบผม

Enhanced Live Wallpaper Metadata

ตอนนี้สามารถกำหนด Metadata เพิ่มเติมให้กับ Live Wallpaper ได้แล้ว แสดงผลเป็นแบบนี้ครับ

แค่นี้แล แฮ่ๆ

Round Icon Resources

Launcher บางตัวเช่น Google Pixel Launcher ตอนนี้เลือกที่จะใช้ไอคอนแบบวงกลม (Round Icon) เพื่อให้โดยรวมดูสวยงาม

กูเกิลเลยแสดงความอำมาตย์ประกาศเพิ่ม attribute ชื่อ android:roundIcon ให้ใส่ได้ใน manifest ควบคู่กับ android:icon

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.inthecheesefactory.lab.thread">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:roundIcon="@mipmap/ic_launcher_round"        ...>        ...    </application></manifest>

การทำงานคือถ้า Launcher นั้นๆใช้ไอคอนแบบวงกลมมันก็จะไปเลือกไอคอนจาก android:roundIcon แต่ถ้าใช้ไอคอนแบบธรรมดามันก็จะดึงจาก android:icon ง่ายๆแบบนี้นั่นเอง

คำถามคือถ้า Launcher อยากจะใช้ไอคอนแบบวงกลมแต่เราดันไม่ประกาศ android:roundIcon ไว้ มันจะแสดงผลยังไง?

คำตอบคือมันจะใช้ไอคอนปกติ ไม่ได้เอาไปใส่ไว้ในวงกลมโดยอัตโนมัติเน้อ

ดังนั้นเพื่อความสวยงามและกลมกลืน (Consistency) เราเลยแนะนำว่าให้ทำไอคอนวงกลมเพิ่มขึ้นมาด้วยแล้วชีวิตจะดีย์~~~~

Storage Manager Intent

เพิ่ม Intent ที่ชื่อว่า ACTION_MANAGE_STORAGE ที่จะพาผู้ใช้ไปสู่หน้า Free up space ในกรณีที่แอปคุณต้องการใช้พื้นที่ในเครื่องแต่พื้นที่มีไม่พอใช้ก็สามารถยิง Intent ตัวนี้เพื่อให้ผู้ใช้ไปเคลียร์พื้นที่ว่างเพิ่มได้ทันทีครับ

Improved VR Thread Scheduling

VR Mode เป็นสิ่งที่เพิ่มขึ้นมาใน Android 7.0 Nougat แต่ปัญหาคลาสสิคหลักๆของ VR ที่เจอกันอยู่ทุกวันนี้คือเรื่องของ Latency (ดีเลย์) ซึ่งทำให้การเล่น VR นั้นปวดหัวมากๆ

Android 7.1 ก็เลยเพิ่มคำสั่ง setVrThread ขึ้นมาเพื่อให้กำหนด Thread ที่เราสร้างขึ้นมาให้ทำงานแข่งกับ Thread อื่นได้มากกว่าปกติ ซึ่งส่งผลให้ลด Latency ได้มากนั่นเอง

ActivityManager.setVrThread(threadId);

อันนี้เป็น Document ของคำสั่งนี้

Enable more aggressive scheduling for latency-sensitive low-runtime VR threads. Only one thread can be a VR thread in a process at a time, and that thread may be subject to restrictions on the amount of time it can run. To reset the VR thread for an application, a tid of 0 can be passed.

ทั้งนี้ทั้งนั้น คำสั่งนี้จะไม่ส่งผลถ้าไม่ได้อยู่ใน VR Mode ครับ

Demo User Hint

แอนดรอยด์สามารถรันในโหมด Demo User ได้ ซึ่งตอนนี้เราสามารถเช็คได้ว่าเครื่องรันอยู่ในโหมดนี้หรือไม่ผ่านคำสั่งนี้

UserManager.isDemoUser()

แค่นี้เลย ไม่มีอะไรมากครับ

New Screen Densities for Wear Devices

เปิด Density เพิ่มอีก 3 ตัวที่มีใช้ใน Android Wear Devices

- DENSITY_260

- DENSITY_300

- DENSITY_340

ขอให้มีความสุข ...

Timeline และการอัปเดต

Preview แรกปล่อยออกมาแล้วตอนนี้ (ตุลาคม) และจะปล่อยเป็นเวอร์ชัน Final เดือนธันวาคมนี้ครับ แต่อย่างไรก็ตาม ถึงมันจะยังเป็น Beta Quality อยู่ แต่ API ที่ออกมาตอนนี้นั้น Final แล้ว สามารถเริ่มพัฒนาเพื่อให้สนับสนุน API Level 25 อย่างสมบูรณ์ได้ตั้งแต่วันนี้ครับ

โดย Preview 1 ที่ปล่อยมาตอนนี้จะมีให้ใช้บน Nexus 5X, Nexus 6P และ Pixel C

ตัว Preview 2 ที่จะปล่อยในเดือนพฤศจิกายนจะมีให้ใช้บน Nexus 6, Nexus 5X, Nexus 6P, Nexus Player, Nexus 9, and Pixel C และ General Mobile 4G (Android One)

ส่วน Final Release ที่ปล่อยในเดือนธันวาคมจะมีอัปเดตให้ Nexus 6, Nexus 5X, Nexus 6P, Nexus Player, Nexus 9, and Pixel C, General Mobile 4G (Android One), Pixel และ Pixel XL ครับ

สำหรับผู้ที่ถือครองมือถือข้างต้นสามารถเข้าร่วม Beta Program ได้เพื่อรับอัปเดตผ่าน OTA หรือถ้าอยากจะสนุกก็โหลด ROM มา Flash ลงเองก็ได้ครับ จะมีปล่อยให้ตามช่วงเวลา ลองดูๆ

ส่วนมือถือรุ่นอื่นๆที่นอกเหนือจากรุ่นเหล่านี้ ... ตัวใครตัวมันจ้า (-0--)/


คร่าวๆก็เท่านี้ครับ นักพัฒนาแอปแอนดรอยด์ทั้งหลายเริ่มลุยได้เลยยยยย ขอให้สนุกครับ ^_^

บทความที่เกี่ยวข้อง

Jun 6, 2016, 14:08
38242 views
บันทึกการพัฒนาระบบ Online Learning Platform ตอนที่ 3: วาง Server Stack ด้วย Docker
Oct 29, 2016, 22:07
24324 views
แก้ปัญหา USB WiFi Dongle ชิปเซต Ralink ใช้งานไม่ได้บน Hackintosh
0 Comment(s)
Loading