ActionBarCompat 【Basic】

26 September 2013

ActionBarCompat是Google近期推出的lib,
目的是讓3.0以下的裝置也能夠使用ActionBar,
其實在ActionBarCompat推出之前,
大部份的developer應該都是使用ActionBarSherlock作為backward compatibility的lib!

但我一直都沒有把ActionBarSherlock拿來放在一個production的app,
原因是因為可能有一天它就失去支援,那到時候我就很麻煩,可能要跳進去自己改了!
但現在有了ActionBarCompat,且是Google推出的,支援度應該會較好!

這篇就是教大家怎麼整合最基本ActionBarCompat,
讓大家的app可以向下相容actionbar!


首先要先import ActionBarCompat這個lib,
而ActionBarCompat的source code會放在android sdk之中,
路徑應該為:
android-sdk-macosx/extras/android/support/v7/appcompat


接著在你的project點選右鍵->選擇Properties->選擇Android
會看到下圖



接著點選上圖中的Add按鈕,就會看到下圖,
看到下圖後,請選擇android-support-v7-appcompat,


選完以後就會看到下圖,請按下按鈕OK!



完成以後就能在你的project裡面使用actionbar了,只差一些設定了!

1. 先換Style

記得換掉values, values-v11, values-v14裡面所有的style.xml,
把AppBaseTheme的parent改成下列的example,
如果你不是一個新的project,你可能有自己的theme,那麼就把他的parent換成下面的吧!
<style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light">
</style>


2. extend ActionBarActivity

接著要把你主要的Activity換成extend ActionBarActivity,如下
package net.kenyang.practiceactionbarcompat;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}



3. 修改menu/main.xml

你的project應該會有一個預設的main.xml
那麼你需要做兩件事:

  • 將root element新增一個attribute
  • element新增一個attribute

如下:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:actionbar="http://schemas.android.com/apk/res-auto" >

<item
android:id="@+id/action_settings1"
android:icon="@drawable/ic_launcher"
android:orderInCategory="0"
actionbar:showAsAction="ifRoom"
android:title="@string/action_settings"/>
<item
android:id="@+id/action_settings2"
android:orderInCategory="100"
android:showAsAction="never"
android:title="@string/action_settings"/>

</menu>


注意上面的例子,
第一個item和第二個item有一個attribute不一樣!
就是showAsAction,一個是actionbar,另一個是android
如果你想要這顆menu獨立顯示為一個action item的話,
請改成actionbar,否則用android的話,你永遠看不到那顆action item,
如果是android 3.0以上,它會被隱藏在下圖中右上角的點點點之中!
如果是android 2.x系列,就會在menu裡面!



所以請都改成actionbar:showAsAction!
最後請注意actionbar:showAsAction有很多個選項,
如ifRoom, never, always...etc
可以自己去試試看各自的差別!


只要完成上述三個步驟!
就完成一個很基本的actionbarcompat!
且可以向下相容至2.x的device上!!


在後面幾篇會告訴大家

  • 如何用actionbarcompat加入tabs!(這裡)
  • swipe to switch tabs.
  • style actionbar










read more »


Using Raspberry PI as Wireless AP

02 September 2013


Before we start, please make sure your system requirements.

What's do you need?
  1. A Raspberry PI
  2. A boot SD card with default Raspberry PI image iso (Raspbian "wheezy")
  3. A USB WiFi device which support "Access Point" mode and the driver is rtl8192cu


1. Install the necessary packages
sudo apt-get install hostapd bridge-utils

2. Configure
sudo vim /etc/default/hostapd
Add the following line to the bottom of the file.
DAEMON_CONF="/etc/hostapd/hostapd.conf"


Modified the config file
sudo vim /etc/hostapd/hostapd.conf
Add the following lines to the file.
You can change the "ssid", "wpa_passphrase".
Most the others examples use nl80211 as their driver, but Raspberry PI don't have the driver (default).
Therefore I can't start the service hostapd.
However I found some people use rtl871xdrv and they have the related hostapd binary file that supports rtl driver.
So... I use rtl871xdrv.
interface=wlan0                                                                                                                 
driver=rtl871xdrv
ssid=KenRaspberryPiAP
hw_mode=g
channel=6
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=safesync
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP



3. Change network interface setting
sudo vim /etc/network/interfaces


auto lo
auto br0
iface lo inet loopback
iface br0 inet dhcp
bridge_fd 1
bridge_hello 3
bridge_maxage 10
bridge_stp off
bridge_ports eth0 wlan0

allow-hotplug eth0
iface eth0 inet manual
allow-hotplug wlan0
iface wlan0 inet manual



4. Replace the hostappd binary

Download
cd /usr/sbin
rm -f hostapd
wget http://dl.dropboxusercontent.com/u/13846584/hostapd

Change Permission
sudo chown root:root hostapd
sudo chmod 755 hostapd



5. Reboot
sudo reboot now


After reboot, you should see your own Raspberry PI Access Point(AP).











read more »


Android GCM (Google Cloud Messaging):Android Client Side

01 July 2013

前篇文章已經說明過GCM,以及和前身C2DM的差異!

這篇主要會講解Android端如何實作,其實和以前在C2DM時差不多
如果要看差異以及一些參數說明,可以到下面幾篇文章去看
第一篇在講參數說明
第二篇在講流程說明



這裡就直接進入主題,
首先一樣要在Manifest檔案中,要有下列的permission,
    <permission android:name="net.kenyang.gcm.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="net.kenyang.gcm.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />


接著就是要新增一個receiver,
而receiver必須聆聽兩個事件,分別為receive以及registration!
    <receiver
android:name="net.kenyang.gcm.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="net.kenyang" />
</intent-filter>
</receiver>


再寫code之前,請先確定你的eclipse有裝下面的兩個plugin,



安裝完成以後,請去到你的sdk folder裡面找到下面兩個jar檔,並且include他們兩個!

  1. android-sdk-macosx/extras/google/gcm/gcm-client/dist/gcm.jar  
  2. android-sdk-macosx/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar



接著開始寫code,
android分為三個部分,

  1. 向Google 註冊,並且取得registration id
  2. 傳送id給我們的server
  3. 準備接收資料


首先是註冊,記得改成你的sender_id!
sender_id就是你的project id,可以到api console去看你的id,
會有下圖,project number就是你的project id了!

記得把它填入sender_id中!

 

GoogleCloudMessaging gcm;
String SENDER_ID = "xxxxxxxxxxx";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tvRegisterMsg = (TextView) findViewById(R.id.tvRegisterMsg);
context = getApplicationContext();

// initial GCM
gcm = GoogleCloudMessaging.getInstance(this);

// register with Google.
new AsyncTask<Void,String,String>() {
@Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
strRegId = gcm.register(SENDER_ID);
msg = "Device registered, registration id=" + strRegId;

// send id to our server
sendRegIdToServer(strRegId);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}

@Override
protected void onPostExecute(String msg) {
tvRegisterMsg.append(msg + "\n");
}

}.execute(null, null, null);
}




接著是第二部分,把id傳送到我們的server,
 private void sendRegIdToServer(final String strId) {
new AsyncTask<Void,String,String>() {
@Override
protected String doInBackground(Void... params) {
String strResponseCode = "";
try {
HttpRequestBase request =null;
request = new HttpGet(new URI("http://your_server/storeRegId?id="+strId));

request.addHeader("User-Agent", "Android");
HttpResponse response = Connection.connect((HttpGet)request);

strResponseCode = String.valueOf(response.getStatusLine().getStatusCode());
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return strResponseCode;
}

@Override
protected void onPostExecute(String msg) {
tvRegisterMsg.append("status code: " + msg + "\n");
}

}.execute(null, null, null);
}


到了此步驟你在畫面上應該會看到registration_id和status code是200!
如果是上述的結果的話,就代表你向Google註冊成功了!以及也把registration id傳送給我們的server知道了!

接著就來implement receiver的部分!
這個receiver會接收來自Google Server的資料,
並且把資料丟到我們的notification bar當中!

public class GcmBroadcastReceiver extends BroadcastReceiver {
static final String TAG = "GCMDemo";
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
Context ctx;

@Override
public void onReceive(Context context, Intent intent) {
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context);
ctx = context;
String messageType = gcm.getMessageType(intent);
if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error: " + intent.getExtras().toString());
} else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED .equals(messageType)) {
sendNotification("Deleted messages on server: " + intent.getExtras().toString());
} else {
sendNotification("Received: " + intent.getExtras().toString());
}
setResultCode(Activity.RESULT_OK);
}

// Put the GCM message into a notification and post it.
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);

PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0,
new Intent(ctx, Main.class), 0);

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("GCM Notification")
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setContentText(msg);

mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}


完成上述以後你就可以開始測試!
只要打開瀏覽器,然後輸入你的site,(http://your_site/send),
接著你的device上應該就會收到訊息了!
訊息會是一個json格式!






read more »