Sean's Note

2016年3月23日 星期三

在 didFinishLaunchingWithOptions 時不使用 storyboard 直接載入 nib 檔

  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] ;

     // Override point for customization after application launch.

     MyViewController *test = [[MyViewController alloc]     initWithNibName:@"MyViewController" bundle:nil];
     UINavigationController *nav = [[UINavigationController alloc]  initWithRootViewController:test];
     self.window.rootViewController = nav;

     [self.window makeKeyAndVisible];

     return YES;
  }

2016年3月20日 星期日

Setting Up Environment for PyQt 5 on Mac with Python 2.7

要用 Python 2.7 來開發 Qt 5,需要安裝下列三樣:
  1. SIP 4.17
  2. PyQt 5.5.1
  3. Qt 5

安裝 sip

  1. 下載 sip-4.17.tar.gz
  2. 解壓縮:tar -xf sip-4.17.tar.gz
  3. 切換目錄到 sip-4.17
  4. python config.py,產生 Makefile
  5. make
  6. make install

安裝 Qt 5

本來是用官網的  Online Installer,但選擇下載 Qt 5.6.1 後,不僅要吃 13.06 GiB,還一直卡在某個進度,只先好作罷。改用 Cakebrew 下載 Qt 5.6.0,而且只要 310.7M。

安裝 PyQt 5.5.1

安裝方式和 sip 一樣
  1. 下載 PyQt-gpl-5.5.1.tar.gz
  2. 解壓縮:tar -xf PyQt-gpl-5.5.1.tar.gz
  3. 切換目錄到 PyQt-gpl-5.5.1
  4. python config.py,產生 Makefile
    • 此時可能會有找不到 qmake 指令的錯誤,qmake 在安裝好的 PyQt 路徑下,設定好路徑就行了。export PATH=/usr/local/Cellar/qt5/5.6.0/bin:$PATH
  5. make  (這裡會需要等一下子)
  6. make install
大功告成! 立馬開啟 PyCharm 來試試第一個程式吧!


Ref: http://www.time-eater.net/?p=302

2016年3月14日 星期一

SetEnvironmentVariable VS getenv

最近發現一個奇怪的現象,
就是在 VS 的 cpp 裡呼叫 SetEnvironmentVariable 設定環境變數,
但從 Python code 裡印出 os.osenviron 卻看不到所設定的變數,
一查才發現,Python 裡的 os.osenviron 是用 getenv實作的。
GetEnvironmentVariable/SetEnvironmentVariable 是 Windows API,
而 setenv/getenv 是 CRT 函式。CRT 在開始的時候便會透過 Windows API GetEnvironmentStrings 將回傳值存入自己的資料結構之下(EX: MSVCR80!environ),
而此後 getenv 便是對這份副本做操作。

結論就是不要混用 SetEnvironmentVariable 和 getenv 啊!!

Ref:
  1. https://bugs.python.org/issue16633
  2. http://blogs.msmvps.com/senthil/2009/10/13/when-what-you-set-is-not-what-you-get-setenvironmentvariable-and-getenv/

2016年3月12日 星期六

System permissions

Android 從 6.0 (API Level 23)開始,把 System permissions 分成了兩類 normal and dangerous permissions。

Normal Permissions 

這類的權限對使用者的隱私或對其他 APP 的操作有極小的影響,所以這類的權限僅需要宣告在 AndroidManifest 裡,在安裝期間系統自然會賦予這些權限給 APP。
  • ACCESS_LOCATION_EXTRA_COMMANDS
  • ACCESS_NETWORK_STATE
  • ACCESS_WIFI_STATE
  • BLUETOOTH
  • BLUETOOTH_ADMIN
  • CHANGE_NETWORK_STATE
  • CHANGE_WIFI_MULTICAST_STATE
  • CHANGE_WIFI_STATE
  • EXPAND_STATUS_BAR
  • INSTALL_SHORTCUT
  • INTERNET
  • KILL_BACKGROUND_PROCESSES
  • MODIFY_AUDIO_SETTINGS
  • NFC
  • SET_ALARM
  • SET_TIME_ZONE
  • SET_WALLPAPER
  • SET_WALLPAPER_HINTS
  • UNINSTALL_SHORTCUT
  • USE_FINGERPRINT
  • VIBRATE
  • WAKE_LOCK
... 等等。

Dangerous permissions

這類的權限對使用者的隱私或對其他 APP 的操作有潛在的影響,所以這類的權限不僅需要宣告在 AndroidManifest 裡,在執行期間,還需要經過使用者的同意。以下的 Permission groups 都是屬於 dangerous permission 的權限:
  • CALENDAR
  • CAMERA
  • CONTACTS
  • LOCATION
  • MICROPHONE
  • PHONE
  • SENSORS
  • SMS
  • STORAGE
Ref:

2016年2月11日 星期四

什麼是 Outlets 和 Referencing Outlets

在 IB 裡的 Show the Connections inspector 頁面,可以看到 Outlets 和 Referencing Outlets,每次基本一定要設定的就是 UIViewController 與 UIView 與之間的連接。

對於 UIViewController 來說,Outlets 可以說是底下的 view 和為 IBOutlet 的那些 properties,所以從圖1 中可以看到 UIViewController 有兩個 Outlets 分別為 view 和 simpleTableView 對應著這個 nib 檔中別名為 View 和 Simple Table View 的元件。而 Referencing Outlets 則為自己被外部的哪些 Objects 所引用,以圖1來說,因為這個 UIViewController 放了一個 UITableView,而 UITableView 擁有 dataSource 和 delegate 的 properties,需要讓他們引用 UIViewController,這也是為什麼需要讓 UIViewController 實作 UITableViewDelegate 和 UITableViewDataSource 。

圖1. File's Owner Connections

另外從 UITableView 的角度來看,他的 Outlets 則是 dataSource 和 delegate,而 Referencing Outlets 則是 simpleTableView。

圖2. UITableView Connections

2016年2月1日 星期一

How to handle app links?

App Link 可以用來讓程式 A 把 程式 B 的某個 Activity 叫起來。
首先,需要在 AndroidManifest.xml 裡的 Activity 區段宣告以下:
<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="myApp" />
        <data android:host="profilePage" />
    </intent-filter>
</activity>
一個 URI 是由以下所組成:
<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]

所以程式 A 就可以透過 URI myApp://profilePage 把程式 B 給叫起來:
// Application A start application B via the intent with URI. 
Intent i = new Intent();
i.setData(Uri.parse("myApp://profilePage"));
// Check whether the intent can be resolved.
if (i.resolveActivity(getPackageManager()) != null) {
  startActivity(i);
}

甚至 URI 後面也可以帶 query:
myApp://profilePage?uid=12&uname=Sean
再透過 Uri 的方法 getQueryParameterNames 和 getQueryParameter 來取得 key 和 value。


測試的方法

測試的方法有三種:
  1. 直接寫程式碼執行。
  2. 用 Android Studio 來執行
    Run -> Edit Configuration... -> Launch Options -> URL
    在 URL 的欄位輸入 URI 然後執行。
    用這個方法的缺點是只能測 action=android.intent.action.VIEW 而且 category=android.intent.category.BROWSER 的 activity。

  3. 透過 adb command line (推薦的方法)
    輸入 adb 指令:
    adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSER -d myApp://profilePage

Ref:
  1. <data>
    https://developer.android.com/guide/topics/manifest/data-element.html
  2. Support HTTP URLs in Your App
    https://firebase.google.com/docs/app-indexing/android/app?hl=zh-tw#reference-the-noindexxml-file
  3. Testing URLs with Android Studio
    https://firebase.google.com/docs/app-indexing/android/test#lint-checks-for-links