2015年6月13日 星期六

Android 開發(一百零二) 利用annotation 處理 enum

在java 中我們常常會使用enum處理多種type,不過android 官方說這種寫法是非常expensive (可能是效率或memory其實我沒有多做研究)所以官方推薦我們利用另外一種寫法,使用annotation來處理

先讓我們看一下下面的程式碼

public class MyMode {
    @IntDef({SUCCESS, NETWORK_FAIL, DATA_ERROR})
    @Retention(RetentionPolicy.SOURCE)
    public @interface APIMode {}

    public static final int SUCCESS = 0;
    public static final int NETWORK_FAIL = 1;
    public static final int DATA_ERROR = 2;

    private int mode = SUCCESS;
    public int getMode(){
        return mode;
    }

    public void setMode(@ APIMode int _mode){
        mode = _mode;
    }

}

我定義了一個MyMode 並且定義了
SUCCESS = 0
NETWORK_FAIL = 1
DATA_ERROR = 2

接著我利用了
@IntDef({SUCCESS, NETWORK_FAIL, DATA_ERROR})
    @Retention(RetentionPolicy.SOURCE)
    public @interface APIMode {}

定義了一個自定義的annotation APIMode 而這個annotation只有在setMode的時候會被用到,這個annotation有什麼功用呢?

主要是在寫程式的時候,我們常常會這樣寫 mymode.setMode(1);
這樣寫程式是可以work的,但是其實很不好讀,對於其他的developer他們還必須進來查看才知道原來setMode(1)是在說 NETWORK_FAIL ,
所以我們該如何避免developer為了方便,而使用這種寫法呢?

我們使用了剛剛的annotation時,如果有人寫同樣的code,我們就可以在android studio上面看到如下圖的提示

而且數字下方會有紅字顯示,這樣developer就比較會去修改它,可惜的是,這種寫法似乎沒有辦法強制build不過(或許使用lint就行)

最後,我覺得這種寫法的確是蠻不錯的,but對於實際在使用上面,感覺大部份的人還是會為了方便而使用enum,不過官方都這麼推薦了,還是可以考慮用看看囉~

Android 開發(一百零一) google play Service Invite friend Api

google play service 在新版本推出了invite friend api ,讓你可以邀請google 上的朋友,
做法其實很簡單,

        Intent intent = new AppInviteInvitation.IntentBuilder(getString(R.string.invitation_title))
                .setMessage(getString(R.string.invitation_message))
                .setDeepLink(Uri.parse("http://tw.91mai.com/ref/0"))
                .build();
        startActivityForResult(intent, REQUEST_INVITE);

使用上面的方式將intent丟出,就會開啟邀請好友的選單,在邀請完成後利用onActivityResult去取得邀請的ids

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d(TAG, "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);

        if (requestCode == REQUEST_INVITE) {
            if (resultCode == RESULT_OK) {
                // Check how many invitations were sent and show message to the user
                // The ids array contains the unique invitation ids for each invitation sent
                // (one for each contact select by the user). You can use these for analytics
                // as the ID will be consistent on the sending and receiving devices.
                String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data);
                showMessage(getString(R.string.sent_invitations_fmt, ids.length));
            } else {
                // Sending failed or it was canceled, show failure message to the user
                showMessage(getString(R.string.send_failed));
            }
        }
    }

當你將邀請送出之後,你的朋友就會收到邀請下載的信,

out16.gif
out16.gif

如上方兩張圖,左邊是送出邀請的功能
右邊是好有收到邀請時,安裝後開啟時我們可以從intent中取得相關的link and id

為了要能夠在安裝時取得google play store送給我們的廣播,我們必須註冊receiver

    <receiver
           android:name="com.google.android.gms.samples.appinvite.ReferrerReceiver"
            android:exported="true"
            tools:ignore="ExportedReceiver">
            <intent-filter>
                <action android:name="com.android.vending.INSTALL_REFERRER" />
            </intent-filter>
        </receiver>

接著我們可以利用下方的程式碼取得相關的資訊

if (!AppInviteReferral.hasReferral(intent)) {
            Log.e(TAG, "Error: Intent does not contain App Invite");
           
// Extract referral information from the
String invitationId = AppInviteReferral.getInvitationId(
String deepLink = AppInviteReferral.getDeepLink(intent);

如果我們不需要track 成效的話,邀請的功能在這邊已經結束了
接下來要講的就是,我們可以利用google play service新的api 來trace我們的成效

在使用朋友贈送的信件開啟我們的app之後,我們可以利用google play service來track成效,

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .enableAutoManage(this, 0, this)
                .addApi(AppInvite.API)
                .build();

利用AppInvite.API  在api connect成功之後


  String invitationId = AppInviteReferral.getInvitationId(intent);

        // Note: these  calls return PendingResult(s), so one could also wait to see
        // if this succeeds instead of using fire-and-forget, as is shown here
        if (AppInviteReferral.isOpenedFromPlayStore(intent)) {
            AppInvite.AppInviteApi.updateInvitationOnInstall(mGoogleApiClient, invitationId);
        }

        // If your invitation contains deep link information such as a coupon code, you may
        // want to wait to call `convertInvitation` until the time when the user actually
        // uses the deep link data, rather than immediately upon receipt
        AppInvite.AppInviteApi.convertInvitation(mGoogleApiClient, invitationId);

我們可以取得inviteationId ,然後利用 updateInvitationOnInstall 告訴google analytics這個user已經成功安裝.

假設這個邀請是希望新安裝的user可以使用優惠券,那我們可以利用
convertInvitation告訴google analytics有多少使用者安裝後並且成功使用優惠券
也就是可以取得轉換率(我是這樣理解的XD)



最後,由於我是直接拿google 的sample就直接附上相關的網址吧,
有興趣的人可以去那邊做更深入的了解
https://developers.google.com/app-invites/android/guides/app?configured=true

然後下面的網址是appinvite設定的小幫手,在裡面跟著步驟設定之後,你google developer console的設定也會設定完成,個人覺得這個新界面真的是好用很多,大家可以參考看看
https://developers.google.com/mobile/add?platform=android&cntapi=appinvites&cnturl=https:%2F%2Fdevelopers.google.com%2Fapp-invites%2Fandroid%2Fguides%2Fapp%3Fconfigured%3Dtrue%23add-config&cntlbl=Continue%20Adding%20App%20Invites



2015年6月10日 星期三

Android 開發 (一百) What's New in M Permission

在M的版本關於permission做了一個很大幅度的修改,原本的permission在下載時會出現如下方的提示

Screenshot_2015-06-09-22-52-56.png

使用者看到這個提示一定會問的問題是,為什麼需要那麼多權限,你是不是拿了這些權限去做了其他奇怪的事情?
這對app的下載造成了一定的阻礙

還有另一個問題就是,假設新版本比舊版本多了一個權限那麼app就無法自動更新下載,必須使用者確認才有辦法更新app

舉個例子來說
我新增了一個很強大的功能,而且我也如期完成了這個功能,我們行銷打算開始跑新功能的活動,由於我新增了一個權限造成這個版本的普及率要多一個月甚至更久才能普及,那行銷是否就無法如期去跑這個活動.

這其實對許多公司來說都是很大的痛點,明明功能就已經完成了,卻由於沒有良好的普及率造成活動無法如期舉行.

so...針對這兩個問題

android M 提出了新的解決方案

out16.gif

上面的圖示你可以注意到,下載的時候不再有permission的視窗跳出,

其實你不需要擔心,permission並不是不見了,而是只有在需要的時候才會跳出
讓我們再看看下面的圖片

out16.gif

假設上面的按鈕需要使用到phone calls and camera的權限,我可以在使用者點擊的時候才去檢查是否擁有這些權限,如果沒有android會跳出如上的提示視窗跟使用者要求權限,只有在permission 被允許之後,我們才能繼續做後續的事情,例如錄影...

其實這對使用者的經驗是一大提升,因為使用者可以很確切地知道你的權限是用在哪個地方,當然前提是你沒有一打開app就把所有權限一次拿完XD

不過由於這些使用者經驗的提升,也造成了程式碼上一定幅度的修改

首先以上面的例子來說當button click我必須做檢查我是否擁有這個權限

 if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {

如果沒有權限我們可以利用requestPermissions來取得我們需要的權限
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},
                            PERMISSION_REQUEST_PHONE);


requestPermissions之後會跳出上方的提示視窗,必須等使用者按下allow or deny之後才會產生callback

  @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

我們可以在onRequestPermissionsResult處理相關的動作,例如當permission成功取得時應該顯示啥,或者當沒有成功取得權限時應該怎麼辦之類的動作

其實以一個開發者的角度來說,雖然有那麼多的好處,但是這其實提升了程式的複雜度,讓原本的流程又多出了一個callback 判斷時間點,真是有利必有弊啊

不過目前這個功能並不是強制性的,所以舊app還是可以走原本的邏輯,也就是下載時讓使用者看到所有個權限,並且一次性的取得所有權限

但是如果要使用新的功能則必須將下面三個設定都使用新的設定才行
compileSdkVersion 'android-MNC'
minSdkVersion "MNC"
targetSdkVersion "MNC"

如果使用了MNC版本請記得一定要做permission check 否則就會出現

螢幕快照 2015-06-03 下午9.56.53.png


最後還是要附上sampel code
https://github.com/nightbear1009/M-Permission