Sean's Note

2014年10月24日 星期五

[Android 5.0] Vector Drawables

從 Android 5.0 開始,Drawable 新增了可縮放向量圖形 (SVG) 的用法,

優點是可以任意縮放圖片卻不會失真。

只要在 XML 檔裡定義即可使用,下面是官方所提供的範例(改成紅色的):

<!-- res/drawable/heart.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- intrinsic size of the drawable -->
    android:height="256dp"
    android:width="256dp"
    <!-- size of the virtual canvas -->
    android:viewportWidth="32"
    android:viewportHeight="32">

  <!-- draw a path -->
  <path android:fillcolor="#ff0000" 
           android:pathdata="M20.5,9.5
                             c-1.955,0,-3.83,1.268,-4.5,3
                             c-0.67,-1.732,-2.547,-3,-4.5,-3
                             C8.957,9.5,7,11.432,7,14
                             c0,3.53,3.793,6.257,9,11.5
                             c5.207,-5.242,9,-7.97,9,-11.5
                             C25,11.432,23.043,9.5,20.5,9.5z">
  </path>
</vector>
圖1. Heart


Vector 詳細有哪些屬性可以設定,可以參考新增的 VectorDrawables 類別

pathdata 中語法和 SVG 的語法一樣 M 指的是 moveto,C 是 curveto,z 是 closepath,

大寫為絕對位置,小寫為相對位置,更多 pathdata 的用法可以直接參考 SVG 的文件

畫了個西瓜來當練習:

<!-- res/drawable/heart.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
           android:height="256dp" 
           android:viewportheight="32" 
           android:viewportwidth="32" 
           android:width="256dp" >

  <!-- watermelon-->
  <path android:fillcolor="#ff0000" 
           android:strokecolor="#086D08" 
           android:strokewidth="1"
           android:pathdata="M3,3 
                             C3,13 23,13 23,3"  />
  
  <!-- seed-->
  <path android:fillcolor="#000000" 
           android:strokecolor="#000000" 
           android:strokewidth="0.5"
           android:pathdata="M5,4 
                            a0.2,0.2 0 1,0 0.4,0 
                            a0.2,0.2 0 1,0 -0.4,0"  />
  
  <!-- seed-->
  <path android:fillcolor="#000000" 
           android:strokecolor="#000000" 
           android:strokewidth="0.5"
           android:pathdata="M8,6 
              a0.2,0.2 0 1,0 0.4,0 
              a0.2,0.2 0 1,0 -0.4,0"  />
  
  <!-- seed-->
  <path android:fillcolor="#000000"
           android:strokecolor="#000000" 
           android:strokewidth="0.5" 
           android:pathdata="M12,8 
              a0.2,0.2 0 1,0 0.4,0 
              a0.2,0.2 0 1,0 -0.4,0"  />
    
  <!-- seed-->
  <path android:fillcolor="#000000" 
           android:strokecolor="#000000" 
           android:strokewidth="0.5"
           android:pathdata="M14,5 
                            a0.2,0.2 0 1,0 0.4,0 
                            a0.2,0.2 0 1,0 -0.4,0"  />
    
  <!-- seed-->
  <path android:fillcolor="#000000" 
           android:strokecolor="#000000" 
           android:strokewidth="0.5"           
           android:pathdata="M18,6 
                            a0.2,0.2 0 1,0 0.4,0 
                            a0.2,0.2 0 1,0 -0.4,0"  />
    
  <!-- seed-->
  <path android:fillcolor="#000000" 
           android:strokecolor="#000000" 
           android:strokewidth="0.5"
           android:pathdata="M20,4 
                             a0.2,0.2 0 1,0 0.4,0 
                             a0.2,0.2 0 1,0 -0.4,0"  />


圖2. Watermelon


2014年10月22日 星期三

Use Enum to implement finite state machine

範例:
public enum StateMachine {
 
    INITIAL {
        @Override
        public StateMachine next() {
            return SECOND;
        }
    },
    SECOND {
        @Override
        public StateMachine next() {
            return THIRD;
        }
    }
    // ... other states
    
    public abstract StateMachine next();
    
    // ... other methods
}

2014年10月15日 星期三

How to prevent the view from restoring it's state after restart?

View 有個屬性 android:saveEnabled,預設為 true,只要把它設成 false,
系統就不會幫你回復它的狀態(View.onSaveInstanceState() 不會被呼叫),
另外要注意的是,該 flag 僅對自己有效,不會影響其 childs。

Ref: http://developer.android.com/reference/android/view/View.html#attr_android:saveEnabled


2014年10月1日 星期三

Running C/C++ on Android with NDK [Updated]

需要準備的環境

  1. Eclipse + Android SDK: 基本的 Android 開發環境
  2. Android NDK: 讓 Android 能透過 JNI 去執行 C/C++ 語言的必要套件
  3. Eclipse NDK: Eclipse 的 plugin 套件,方便管理與建立 NDK 的專案
    Work with: https://dl-ssl.google.com/android/eclipse/
    安裝好之後要設定 Android NDK 的路徑:
    Window -> Preferences -> Android -> NDK -> NDK Location:

    如果 NDK Path 擺在 Program Files 或 Program Files (x86) 底下,在 Build Project 時,
    可能會出現以下錯誤:
    "C:\\Program Files (x86)\\Android\\android-ndk-r10b\\ndk-build.cmd" all
    ERROR: NDK path cannot contain any spaces!

    解決方法:
    C:\Program Files -> C:\progra~1
    C:\Program Files (x86) -> C:\progra~2
Note: NDK r9 的版本之後已內建 toolchains,所以不需要另外安裝 Cygwin。

新建一個專案


新增一個  C:\Program Files (x86)\Android\android-ndk32-r10b\samples\hello-jni 專案,

然後在 Project 上按右鍵 -> Android Tools -> Add Native Support...

除錯


除錯時要將 Project -> Properties -> C/C++ Build -> Builder Settings -> Build Command:

加上 ndk-build NDK_DEBUG=1,建置時才會產生除錯所需要的檔案。

在這之後可能會遇到兩個問題:
  1. 無法在 .c 或 .cpp 檔上設置中斷點?
    Run -> Breakpoint Types  -> C/C++ Breakpoints
  2. 設完中斷點後,卻沒有停在中斷點上?
    No symbol table is loaded. Use the "file" command.
    由於 GDB debugger 需要一點時間運算,而 GDB 可能還沒載入 Library 的 Symbol,
    如果中斷點設得太早可能會被忽略,
    解決的辦法請參考:
    http://www.codeproject.com/Articles/493043/Why-your-Android-NDK-breakpoints-might-fail-and-ho

2014年9月12日 星期五

New Android Application - appcompat_v7

至從更新 ADT 到某版之後,每次建立新專案都會自動蹦出一個 appcompat_v7 專案,
後來找到解決的方式,只要在建立新專案時,最低 SDK 版本 (Minimum Required SDK)
指定為 API 14(即 Android 4.0),就不會出現appcompat_v7。

2014年8月11日 星期一

How to get an unique ID from Android device?

我們有時候會想要取得一組 UID,來判斷是否為不同的 Android 裝置,除了

Java 本身提供的 UUID,是不是還有其他的方法呢?

以下整理了五種方法(Test with Sony ZL 4.4.2):
  1. UUID
    利用 UUID 類別的方法 randomUUID() 產生一組 128-bit 組成的 UUID:
    String uuid = UUID.randomUUID().toString();
    Log.d(TAG, "UUID: " + uuid); // UUID: faffffe6-a504-4252-b92a-c5b555811123
    

  2. IMEI/MEID/ESN
    利用 TelephonyManager 類別的方法 getDeviceId(),在 GSM 的手機上取得 IMEI 或
    在 CDMA 的手機上取得 MEID 或 ESN 的值:
    // Required permission: android.permission.READ_PHONE_STATE
    
    TelephonyManager telManager=(TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
    String imei = telManager.getDeviceId();
    Log.d(TAG, "IMEI: " + imei); // IMEI: 3553XXXXXXXXXXX
    
    

  3. Android ID
    取得由靜態類別 Secure 的靜態變數 ANDROID_ID 所表示的 16進位字串。
    String androidID = Secure.getString(this.getBaseContext().getContentResolver(), Secure.ANDROID_ID)
    Log.d(TAG, "Android ID: " + imei); // Android ID: 826458105eXXXXXX
    
    
    Note: 有些廠商的手機的 Android ID 有重複出現的情況,而且一但使用者回復原廠設定
    時,此 ID 有可能會改變。

  4. Wifi Mac
    透過 WifiInfo 類別的方法 getMacAddress() 取得 Mac Address:
    // Required permission: android.permission.ACCESS_WIFI_STATE
    
    WifiManager wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
    WifiInfo wifiInfo = wifiManager.getConnectionInfo();
    Log.d(TAG, "MacAddress: " + wifiInfo.getMacAddress()); // MacAddress: b4:XX:XX:XX:42:fc
    
    
    Note: 沒有開啟 Wifi 時可能會回傳 null,但 Sony ZL 是都要的到。

  5. Serial
    透過 Build 類別的靜態變數 SERIAL 取得手機的硬體序號:
    String buildSerial = android.os.Build.SERIAL;
    Log.d(TAG, "BuildSerial: " + buildSerial); // BuildSerial: EP7323XXXX
    
    
    Note: API Level 9 以上才支援,不保證每台裝置都有。

Ref: http://android-developers.blogspot.tw/2011/03/identifying-app-installations.html

RFC 1867 - Form-based File Upload in HTML

在 RFC 1867 被提出之前,Form 的 INPUT 的類別僅有八種: CHECKBOX, HIDDEN, IMAGE,
PASSWORD, RADIO, RESET, SUBMIT, TEXT。在這之中似乎少了 FILE 可以讓使用者上傳檔
案。於是這份 RFC 便提出了兩個提案:
  1. 使 INPUT 多一個 FILE 的選項。
  2. 允許 INPUT 有一 ACCEPT 的屬性,指定可上傳的檔案類別。
由於原本的 application/x-www-form-urlencoded ,在傳輸大量的位元資料效率不彰,
所以也定義了新的 MIME 類別 multipart/form-data。
當撰寫 HTML 的程式設計師想向使用者請求一或多個檔案便可以寫成:
 <FORM ACTION="http://server.dom/cgi/handle" 
  ENCTYPE="multipart/form-data"
  METHOD=POST>
  What is your name? <INPUT TYPE=TEXT NAME=submitter>
  What files are you sending? <INPUT TYPE=FILE NAME=pics>

multipart/form-data 根據表單欄位包含了許多個部分,每個部分都應帶有:
  • Header: content-disposition: form-data; name="xxxxx",name 即是該欄位在表單中的名稱。
  • boundart: 自定義但不能出現在內文的的邊界字串。

實際從 Client 端傳回 Server 端的資料如下:
Content-type: multipart/form-data, boundary=AaB03x

        --AaB03x
        content-disposition: form-data; name="field1"

        Joe Blow
        --AaB03x
        content-disposition: form-data; name="pics"; filename="file1.txt"
        Content-Type: text/plain

         ... contents of file1.txt ...
        --AaB03x--

如果使用者選擇了多個檔案,如多選了一個 file2.gif,則資料如下:
Content-type: multipart/form-data, boundary=AaB03x
       
        --AaB03x
        content-disposition: form-data; name="field1"

        Joe Blow
        --AaB03x
        content-disposition: form-data; name="pics"
        Content-type: multipart/mixed, boundary=BbC04y

        --BbC04y
        Content-disposition: attachment; filename="file1.txt"

        Content-Type: text/plain

        ... contents of file1.txt ...
        --BbC04y
        Content-disposition: attachment; filename="file2.gif"
        Content-type: image/gif
        Content-Transfer-Encoding: binary

          ...contents of file2.gif...
        --BbC04y--
        --AaB03x--

 Ref:
  1. http://www.faqs.org/rfcs/rfc1867.html
  2. http://blog.zhaojie.me/2011/03/html-form-file-uploading-programming.html