Google+ Followers

2018年10月11日 星期四

PHP 開發筆記 - 判斷瀏覽器語系機制

就 Javascript 來說,很方便:

var userLanguage = navigator.language || navigator.userLanguage;

而 server site 就靠 $_SERVER['HTTP_ACCEPT_LANGUAGE'] 啦,但他的格式還會有語系偏好比重,需要小小處理一番:

//
// $_SERVER['HTTP_ACCEPT_LANGUAGE'] == 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7';
//
function _detect_browser_language($_SERVER_VAR, $system_prefer = array( 'en' => 1, 'cn' => 1 )) {
if (isset($_SERVER_VAR['HTTP_ACCEPT_LANGUAGE'])) {
$langs = array();
foreach(explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $entry) {
$t1 = explode(';', $entry);
$cnt = count($t1);
if ($cnt == 2) {
$t2 = explode('=', $t1[1]);
if (count($t2) == 2)
array_push($langs, array($t1[0], floatval($t2[1])));
else
array_push($langs, array($t1[0], 1.0));
} else if ($cnt == 1)
array_push($langs, array($t1[0], 1.0));
}
function lang_prefer_sort($a, $b) {
if( $a[1] == $b[1] ) return 0;
if( $a[1] > $b[1] ) return -1;
return 1;
}
usort($langs, 'lang_prefer_sort');
foreach($langs as $lang_info) {
if (is_array($lang_info)) {
if (isset($system_prefer[$lang_info[0]]))
return $lang_info[0];
$checker = explode('-', $lang_info[0]);
if (count($checker) == 2) {
if (isset($system_prefer[$checker[0]]))
return $checker[0];
else if ($checker[0] == 'zh')
return 'cn';
}
}
}
}
return 'en';
}

2018年9月18日 星期二

Android 開發筆記 - MenuItemCompat.getActionProvider return null

週末練練 Android 手感,試試 Cast SDK ,結果連最簡單的 CastButton 都弄不出來,一直碰到:

java.lang.IllegalArgumentException: menu item with ID ###### doesn't have a MediaRouteActionProvider.

跑去看了一下 CastButtonFactory.setUpMediaRouteButton 程式碼:

    public static MenuItem setUpMediaRouteButton(Context context, Menu menu, int i) {
        zzac.zzdn("Must be called from the main thread.");
        zzac.zzw(menu);
        CastContext sharedInstance = CastContext.getSharedInstance(context);
        MenuItem findItem = menu.findItem(i);
        if (findItem == null) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "menu doesn't contain a menu item whose ID is %d.", new Object[]{Integer.valueOf(i)}));
        }
        MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat.getActionProvider(findItem);
        if (mediaRouteActionProvider == null) {
            throw new IllegalArgumentException(String.format(Locale.ROOT, "menu item with ID %d doesn't have a MediaRouteActionProvider.", new Object[]{Integer.valueOf(i)}));
        }
        mediaRouteActionProvider.setRouteSelector(sharedInstance.getMergedSelector());
        return findItem;
    }


才確認應該有什麼搞錯了,再追了一下,果真是 menu 的 xml 描述有問題,不小心把 app namespace 弄錯了,錯誤:

xmlns:app="http://schemas.android.com/tools"

正確:

xmlns:app="http://schemas.android.com/apk/res-auto"

就這樣耍廢了兩三個小時。

程式範例也才幾句話:

    @Override public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.navigation, menu);
        CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
                menu,
                R.id.media_route_menu_item);
        return true;
    }


為了 debug :

    @Override public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        getMenuInflater().inflate(R.menu.navigation, menu);

        MenuItem mMenuItem = menu.findItem(R.id.media_route_menu_item);
        if (mMenuItem != null) {
            Log.v("onCreateOptionsMenu", "Found");
            MediaRouteActionProvider mediaRouteActionProvider =
                    (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mMenuItem);

            if (mediaRouteActionProvider != null) {
                Log.v("MediaRouteActionProvider", "Found");
            } else {
                Log.v("MediaRouteActionProvider", "Not Found");
            }
        } else {
            Log.v("onCreateOptionsMenu", "Not Found");
        }
        return true;

2018年9月16日 星期日

刻意練習

刻意練習

幾個月前就買了這本書,當時在衝刺一些點子一直沒去翻。近幾個禮拜刻意讓自己緩下步,甚至週末都沒打開電腦寫任何程式。刻意練習,跟 "一萬小時法則" 有一點關係,最重要的是持續且找到有效精進自己的方式。

讀著這本書,想起自己學資訊的步調,就像寫 Blog 只是為了留下點什麼,都是刻意的足跡。套句高手的說法:看看 stack overflow 就行了啊,幹嘛花時間寫筆記?但也是這種刻意的筆記造就了一些小成果。

最近翻書的最大心得:不小心把目標設定太遠了,導致完全提不起勁去克服。但,太容易得手的目標,也是沒任何效果的。

回過頭來,只能繼續專注。生活存在有太多誘惑了。

2018年8月7日 星期二

攀談



跟幾位老友哈拉幾句,又再次發現空氣的新鮮。後來也才知道,大家進退之間,早已找好了位置。幾天前跟一位在 CIO 熱潮的前同事哈拉,順便細細地思考自己到底在追尋著什麼。

每一次的攀談都是不錯的躍進,可惜的是話上句點後,又瞬間跌落谷底 XD 什麼也沒改變。
老同事推薦翻翻李笑來的 "通往財富自由之路" ,重點落在複利的學習力、好好善用自己的長才去累積價值。

這幾天也認識了一位剛三十歲的女強人,先去泰國學語言幾個月,接著開公司、批深圳的貨來泰國賣,搞通泰國銷售流程、客服跟在地行銷,實在佩服。她不是業務出身,而完全就是同背景的IT系統整合,懂的善用時間不自己造輪(Shopify),懂的分析時勢在東南亞找到適合的位置,就這樣努力衝刺著,著實佩服。雖然跟老友哈拉這案例時,總會回歸到個人到底有多少資源可發揮,但我認為最重要仍是執行力啦。

隨時間流逝,會有越來越多的感觸吧,那可能已經不是後悔了。

2018年7月29日 星期日

[Apple] MacBook Pro 脫膜,在 Apple 台北101 Genius Bar 單日完成維修體驗 (Retina, 13-inch, Early 2015)

Apple 台北101 Apple 台北 101 維修人潮

我習慣買前一代的 Mac 產品,在 2016 年底買了這台 2015 年的產品(直到查詢脫膜才發現自己是 Early 2015 版),以為脫膜跟我無關了 XD 殊不知還是中獎,隨著使用時間開始出現跡象了,剛好這週得閒,就行動衝一發了 XD 不然有了家庭跟小孩,親自送修這種事真的太殺時間了!

整體上,我先打了電話到 Studio A ,得知評估脫膜還得把筆電放在那邊 7-14 天,當下就很三條線,立刻就改打電話去 Apple 台北101 ,看看直營店有沒有什麼差別。只是整個過程我去了三趟 台北 101 ,第一趟誤被台北101接電話的服務員給誤導,我在中午播電話過去,詢問可否現場排隊維修 Mac ,服務員說現在人很少可以試試,直到我到了現場才知道 Mac 維修是最夯的,通常只能預約,不然就排現場(11:00開門),排現場也只是看看有沒有預約取消的額度,並不確定一定可以修到。

隔天吃完早餐報備後,就出發去 101 了,約 10:30 到了,跟著陸客就莫名到了 Apple 台北101 的門口排隊了 (台北購物中心11:00才開門),大概排前五名,就這樣等個30分鐘就可入場維修!等到自己登記完維修後,才發現後排大概至少排了30人在等維修。

這大概就是 Apple Support app 上有維修預約服務,但實際上根本都搶不到只能排現場的現象,不知是不是暑假的關係?為了維修只能排現場了。

後來很順利得知有備料,交完筆電後,很有機會就在當日拿到!至於網路上有的人說可以先去評估維修,接著再等料來再拿去,這件事並不對的,脫膜換螢幕一律要把筆電放在店內等維修,有料無料都還跑會基本的 SOP 做整機檢驗。

所以,脫膜維修要有心理準備,沒筆電就是得停工一陣子。

心得:
  • 脫膜維修流程是會先判斷是否為人員問題,維修不管怎樣都得要與筆電分開一陣子,最慘的是 7-14天,如 Studio A 據說要把螢幕寄回去給原廠評估是否為人為。因此,去直營店好處好上不少,運氣好可以一早進去晚上就拿貨。
  • 下載 "Apple 支援" 或是網頁申請支援,都可以有專人電話服務(2分鐘內播來),但對於 Mac 維修還是走預約制度(且各間店頂多八天內可以選時段),結果台北夏天人多,預約根本選不到,等同只能排現場。排現場會先登記,後續簡訊通知是否輪到你

    Apple 支援 app Apple 台北101 簡訊服務
  • Apple 台北 101 ,建議有需要還是排一早前幾名,投資報酬率高,不然還是等預約