2014年4月16日 星期三

Notification Sample

Notification 模擬結果:
按下按鈕會發出通知和聲響,若程式在特殊手機上執行LED會亮綠燈

MainActivity.java
import android.media.AudioManager;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {
 Context mContext = this;
 EditText etTitle,etText;
 Button btNotify;
 @Override
 protected void onCreate(Bundle savedInstanceState) 
 {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  mContext = this;
  etTitle  = (EditText) findViewById(R.id.xml_etTitle);
  etText  = (EditText) findViewById(R.id.xml_etText);
  btNotify = (Button) findViewById(R.id.xml_btSend);
 }
 
 public void onClick(View view)
 {
  if(view.getId()==R.id.xml_btSend)
  {
   if(etTitle.length()==0 || etText.length()==0)
    return;
   //initial notification manager and audio manager
   NotificationManager nm = (NotificationManager) mContext.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
   NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
   AudioManager mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
   //
   int ring_mode = mAudioManager.getRingerMode();
   int DrawableId = android.R.drawable.sym_call_missed;
   String Title = etTitle.getText().toString();
   String Text = etText.getText().toString();
   //set the activity of the trigger
   Intent intent_missCall= new Intent(mContext, MainActivity.class);
   intent_missCall.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
   intent_missCall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   PendingIntent pIntent = PendingIntent.getActivity(mContext, 0,
      intent_missCall, PendingIntent.FLAG_UPDATE_CURRENT);
   //
   boolean AutoCancel = true;
   boolean OnGoing = false;
   //light the LED. color is green.(It's not action on every cellphone)
   builder.build().ledARGB = 0x00FF00;
   builder.build().ledOnMS = 100;
   builder.build().ledOffMS = 100;
   builder.build().flags = Notification.FLAG_SHOW_LIGHTS; 
   //
   //change the notify effect by checking the ring mode
   if(ring_mode == AudioManager.RINGER_MODE_NORMAL ) {
     Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
     builder.setSound(notification);
   }
   if(ring_mode == AudioManager.RINGER_MODE_VIBRATE) {
     builder.setVibrate(new long[] { 1000, 1000, 1000, 1000, 1000 });
   }
   //
   //set the notification and notify
   builder.setTicker(Title)
   .setContentTitle(Title)
   .setContentText(Text)
   .setSmallIcon(DrawableId)
   .setAutoCancel(AutoCancel)
   .setOngoing(OnGoing)
   .setContentIntent(pIntent);
   nm.notify(0, builder.build());
   //
  }
 }
}

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/xml_btSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/editText1"
        android:layout_marginTop="90dp"
        android:onClick="onClick"
        android:text="Notify" />

    <EditText
        android:id="@+id/xml_etTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:ems="10" >

        <requestFocus />
    </EditText>

    <EditText
        android:id="@+id/xml_etText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/xml_tvNotifyText"
        android:layout_alignParentRight="true"
        android:ems="10" />

    <TextView
        android:id="@+id/xml_tvNotifyText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/xml_tvNotifyTitle"
        android:layout_below="@+id/xml_etTitle"
        android:layout_marginTop="16dp"
        android:text="NotifyText:"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/xml_tvNotifyTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/xml_etTitle"
        android:layout_alignParentLeft="true"
        android:text="Notify Title:"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.notify_test"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />
    <uses-permission android:name="android.permission.VIBRATE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.notify_test.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

2014年4月10日 星期四

GetScreenWidth&Height

獲取Nexus7螢幕尺寸模擬結果:

MainActivity.java
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity 
{
 int width,height,statusBarHight = 0;
 TextView tvHeight,tvWidth;
 @Override
 protected void onCreate(Bundle savedInstanceState) 
 {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //get the screen width and height
  DisplayMetrics dm = new DisplayMetrics();
  getWindowManager().getDefaultDisplay().getMetrics(dm);
  //calculate status bar height
  int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
  if (resourceId > 0)
   statusBarHight = getResources().getDimensionPixelSize(resourceId);
  
        int vWidth = dm.widthPixels;
        int vHeight = dm.heightPixels - statusBarHight;
        if(vWidth < vHeight) 
        {
         width = vWidth;
         height = vHeight;
        } 
        else 
        {
         width = vHeight;
         height = vWidth;
        }
        //show the screen width and height 
        tvHeight = (TextView) this.findViewById(R.id.xml_tvHeight);
        tvWidth = (TextView) this.findViewById(R.id.xml_tvWidth);
        tvHeight.setText("Height: "+height);
        tvWidth.setText("Width: "+width);
 }
}

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/xml_tvHeight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignRight="@+id/xml_tvWidth"
        android:layout_marginTop="172dp"
        android:text="Height: "
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/xml_tvWidth"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/xml_tvHeight"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="29dp"
        android:layout_marginLeft="70dp"
        android:text="Width: "
        android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

2014年4月9日 星期三

Get real external storage path for lower Android 4.4

The new Android version has emulated sdcard.
We usually get external storage path by  API - 'Environment.getExternalStorageDirectory();' .
Sometimes, the API apply a internal storage that means it's emulated external storage.
So I find a way to get real external storage on the Internet, the full code is following:
/**
 * Get real external storage  path.
 * @return Real external storage path or null for no external storage.
 */
private static String getSdcardPath(){
 File file = new File("/system/etc/vold.fstab");
        FileReader fr = null;
        BufferedReader br = null;
        try {
            fr = new FileReader(file);
            if (fr != null) {
                br = new BufferedReader(fr);
                String s = br.readLine();
                while (s != null) {
                    if (s.startsWith("dev_mount")) {
                        String[] tokens = s.split("\\s");
                        String path = tokens[2]; //mount_point
                        br.close();
                        fr.close();
                        return path;
                    }
                    s = br.readLine();
                }
                br.close();
                fr.close();
            }//if (fr != null)
        } 
        catch (FileNotFoundException e) {} 
        catch (IOException e) {} 
        return null;
}
If you want to read file from external storage, remember to add user-permission "android.permission.READ_EXTERNAL_STORAGE".
For writing file, adding user-permission "android.permission.WRITE_EXTERNAL_STORAGE".

If you add "android.permission.WRITE_EXTERNAL_STORAGE", it explicitly add "android.permission.READ_EXTERNAL_STORAGE".

UI onClick callback via Layout.xml file

In my develop habit, I usually use the 'OnClickListener' to listen the button click.
In this topic, I will show you how to listen the button with xml layout file.

The sample code is following:

In MainActivity.java:
package com.example.onclicktest;

import com.example.onclicktest.R;
import android.app.Activity;
import android.widget.Toast;

public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  // TODO Auto-generated method stub
  super.onCreate(savedInstanceState);
  this.setContentView(R.layout.activity_main);
 }
 /**Called when button clicked*/
 public void onClick(View view){
  if(view.getId() == R.id.btn_test_click){
   Toast.makeText(this, "Button Clicked.", Toast.LENGTH_SHORT).show();
  }//if(view.getId() == R.id.btn_write_test_file)
 }
}

In avtivity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    <Button
        android:id="@+id/btn_test_click"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:text="TestBotton"
        android:onClick="onClick" />

</RelativeLayout>
The callback function has two condition:
1. the method must be 'public'.
2. the parameter of the method must only be 'View'.

2014年4月7日 星期一

Android R.java遺失補救方法

假設遇到R.java一直建置不起來的問題
以下是可以嘗試的設定:
1. 將專案清除(Eclipse→Project→Clean)
2. 自動建置方案(Eclipse→Project→Build Automatically)
3. 手動建置方案(Eclipse→Project→Build Project或專案點右鍵選Build Project[如果Build Automatically有選,專案上不會有該選項])
備註:R.java就是記錄layout相關配置參數的檔案,出現錯誤通常都是OO.xml內有錯誤,或是檔名用了大寫。

2014年4月2日 星期三

如何簡單移除UI layout中的Title bar?

以下為剛創立的Android Application project時所產生的基本layout 如下圖:
紅色框框所標註的地方即為Title bar部分,那麼要移除它該怎麼做呢?
很簡單,請依照下圖的指示選取到"AppTheme" => Theme(黑色背景) 或 Theme.Light(白色背景)
=> Theme.Black.NoTitleBar 或 Theme.Light.NoTitleBar 即可

以下為選擇Theme.Black.NoTitleBar的結果圖,惱人的Title Bar消失了~

Get Base Station Information

This topic guild how to get the sdcard information about the base station.

In BaseStationInfoHelper.java
package com.main;

import android.content.Context;
import android.telephony.CellLocation;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;

public class BaseStationInfoHelper {
 //android.permission.ACCESS_COARSE_LOCATION
 public static class BaseStationInfo {
        /** Country */
        public int mcc = -1;
        /** ISP */
        public int mnc = -1;
        /** Base Station Number*/
        public int lac = -1;
        /** Small Region Number */
        public int cid = -1;
    }
    public static BaseStationInfo getSimCardInfo(Context context) {
        final TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);

        BaseStationInfo res = new BaseStationInfo();

        CellLocation clll = telephony.getCellLocation();
        if (clll == null) {
            return res;
        }

        if (clll instanceof GsmCellLocation) {
            GsmCellLocation gsm = (GsmCellLocation) clll;
            int lac = gsm.getLac();
            String mcc = telephony.getNetworkOperator().substring(0, 3);
            String mnc = telephony.getNetworkOperator().substring(3);

            res.cid = gsm.getCid();
            res.mcc = Integer.parseInt(mcc);
            res.mnc = Integer.parseInt(mnc);
            res.lac = lac;
        } else if (clll instanceof CdmaCellLocation) {
            CdmaCellLocation cdma = (CdmaCellLocation) clll;
            int lac = cdma.getNetworkId();
            String mcc = telephony.getNetworkOperator().substring(0, 3);
            String mnc = telephony.getNetworkOperator().substring(3);
            int cid = cdma.getBaseStationId();
            res.cid = cid;
            res.mcc = Integer.parseInt(mcc);
            res.mnc = Integer.parseInt(mnc);
            res.lac = lac;
        }
        return res;
    }
}

In MainActivity.java
package com.main;
import com.example.celllocation.R;
import com.main.BaseStationInfoHelper.BaseStationInfo;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;


public class MainActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //
  TextView tv = (TextView) this.findViewById(R.id.textView1);
  //
  BaseStationInfo info = BaseStationInfoHelper.getSimCardInfo(this);
  String displayString = "";
  if(info != null){
   displayString += "Country mcc = " + info.mcc + "\r\n";
   displayString += "ISP mnc = " + info.mnc + "\r\n";
   displayString += "Base Station Number lac = " + info.lac + "\r\n";
   displayString += "Cell id = " + info.cid + "\r\n";
   //
   tv.setText(displayString);
  }
  else{
   tv.setText("Cannot get the base station information.");
  }
 }

}

In activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="TextView"
        android:textSize="20sp" />

</RelativeLayout>

In AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.celllocation"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name="com.main.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>


執行結果:

2014年4月1日 星期二

Mobile network settings control

This topic is guilding Android developer to change and access mobile network settings in Android phone.

Non-Blocking TCP Server Sample

Non-Blocking TCP Server 模擬結果:
(1) 起始狀態

(2) 啟動Server

(3) Client端連線+發送訊息

(4) Client端離線
備註:當Server端收到Client端的訊息後,會發送回應給Client端


Sample code following:
package com.example.sample_niotcpserver;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;

import org.apache.http.conn.util.InetAddressUtils;

import com.example.sample_niotcpserver.R.id;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity implements OnClickListener {
 //===== Debug Member Variables =====//
 private static String Class_TAG = "MainActivity";
 private static String Debug_TAG = "Debug Use";
 
 //===== UI Member Variables =====//
 private Button    mBtnBind, mBtnClose;
 private TextView  mTvHint, mTvPostMsg;
 private EditText  mEtPort;

 //===== nioTCP Member Variables =====//
 private ServerSocketChannel mServerSocketChannel = null;
 private Selector  mSelector = null;
 private String    mLocalIP = "0.0.0.0";
 private int       mLocalPort = 0;
 private boolean   isRunning = false; // Server open state. 
 
 // Record the remote socket, IP and port.
 private ArrayList<Socket> mSocketList;
 private ArrayList<String> mRemoteIPList;
 private ArrayList<Integer>mRemotePortList;
 
 // When the data can't write to client, need to record the answer data to wait for until it can write. 
 private String   mAnswerTemp = null;
 
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  mBtnBind = (Button) this.findViewById(R.id.xml_btn_bind);
  mBtnBind.setOnClickListener(this);
  mBtnBind.setEnabled(true);
  
  mBtnClose = (Button) this.findViewById(R.id.xml_btn_close);
  mBtnClose.setOnClickListener(this);
  mBtnClose.setEnabled(false);
  
  
  mTvHint = (TextView) this.findViewById(R.id.xml_tv_hint);
  mTvPostMsg = (TextView) this.findViewById(R.id.xml_tv_post_msg);
  
  mEtPort = (EditText) this.findViewById(R.id.xml_et_port);
  mEtPort.setText("8000");
  
 }

 @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;
 }

 // The button listener method. 
 @Override
 public void onClick(View v) {
  switch (v.getId()){
  case id.xml_btn_bind:
   // Close the bind button. 
   mBtnBind.setEnabled(false);
   
   mTvPostMsg.setText("");
   
   mSocketList = new ArrayList<Socket>();
   mSocketList.clear();
   
   mRemoteIPList = new ArrayList<String>();
   mRemoteIPList.clear();
   
   mRemotePortList = new ArrayList<Integer>();
   mRemotePortList.clear();
   
   mLocalIP = getLocalIP();
   mLocalPort = Integer.valueOf(mEtPort.getText().toString());
   
   mTvHint.setText("Starting server...");
   
   new Thread(nioTCPSetverThread).start();
   
   break;
  case id.xml_btn_close:
   // Close the close button. 
   mBtnClose.setEnabled(false);
   
   mTvHint.setText("Closing server...");
   
   // Change server state. 
   isRunning = false;
   
   mSelector.wakeup();

   break;
  }
 }

 private Runnable nioTCPSetverThread = new Runnable(){
  @Override
  public void run() {
   try { 
    // Open the seletor. 
    mSelector = Selector.open();
    
    try {
     // Open the local TCP server socket channel. 
     mServerSocketChannel = ServerSocketChannel.open();
     
     // Configure blocking mode: true(blocking) false(non-blocking). 
     mServerSocketChannel.configureBlocking(false);
     
     // Bind the server with lacal IP address and port. 
     mServerSocketChannel.socket().bind(new InetSocketAddress(mLocalIP, mLocalPort));
     
     // Server socket channel registers OP_ACCEPT command to server selector, and generate a SelectionKey. 
     mServerSocketChannel.register(mSelector, SelectionKey.OP_ACCEPT);
     
     // Change the button state. 
       runOnUiThread(new Runnable(){
      @Override
      public void run() {
       mBtnClose.setEnabled(true);
       mTvHint.setText("Server bind. IP: "+ mLocalIP + " Port: "+ mLocalPort);
     }});

     // Change server state. 
     isRunning = true;
     try {
      while(isRunning == true) {
       
       // Selector try to find the channel which has registered events. 
       mSelector.select();
       
       Set<SelectionKey> ServerChannelKeys = mSelector.selectedKeys();
          Iterator<SelectionKey> keyIter = ServerChannelKeys.iterator();
               
       while (keyIter.hasNext() && (mSelector.isOpen() == true)) {// If keyIter has any event, return true, or retun false. 
        
        // Extract the event into key from keyIter. 
        SelectionKey key = (SelectionKey) keyIter.next();
        
        // Remove keyIter when the key takes the event. 
        keyIter.remove();
        
        if (key.isAcceptable()) {// Accept connect. 
            
         // Accept the client socket from the server channel. 
         final SocketChannel ClientSocketChannel = ((ServerSocketChannel) key.channel()).accept();
         
         // Configure blocking mode: true(blocking) false(non-blocking). 
         ClientSocketChannel.configureBlocking(false);
         
         // Client socket channel registers "OP_READ" command to server selector. 
         ClientSocketChannel.register(mSelector, SelectionKey.OP_READ);
         
         // Add the connectable socket to socket list. 
         if (mSocketList.contains(ClientSocketChannel.socket()) == false) {
         
          mSocketList.add(ClientSocketChannel.socket());
          int socketIndex = mSocketList.indexOf(ClientSocketChannel.socket());
          mRemoteIPList.add(socketIndex, ClientSocketChannel.socket().getInetAddress().toString());
          mRemotePortList.add(socketIndex, ClientSocketChannel.socket().getPort());
         
         }
           
         // Post accept message to UI msg TextView. 
         runOnUiThread(new Runnable(){
         @Override
         public void run() { 
          mTvPostMsg.append("IP:"+ ClientSocketChannel.socket().getInetAddress().toString() + 
                        "(" + ClientSocketChannel.socket().getPort() + ")" + "is online.\r\n");
         }});
          }// if (key.isAcceptable())
        else if (key.isReadable()) {// Receive data. 
        
         ByteBuffer readBuffer = ByteBuffer.allocate(10 * 2048);
         final SocketChannel ClientSocketChannel = (SocketChannel) key.channel();
         int readBuffLen = ClientSocketChannel.read(readBuffer);
         if (readBuffLen < 0) {// Client side socket has disconnected. 
           
          // Remove the disconnectable socket from socket list. 
          if (mSocketList.contains(ClientSocketChannel.socket()) == true) {
             
           final int socketIndex = mSocketList.indexOf(ClientSocketChannel.socket());
           // Post close message to UI msg TextView. 
            runOnUiThread(new Runnable(){
            @Override
            public void run() {
             mTvPostMsg.append("IP:"+ mRemoteIPList.get(socketIndex) + 
                         "(" + mRemotePortList.get(socketIndex) + ")" + "is offline.\r\n");
             mRemoteIPList.remove(socketIndex);
               mRemotePortList.remove(socketIndex);
           }});
          
            mSocketList.remove(ClientSocketChannel.socket());
         
          }
 
          // Close the channel. 
          key.cancel();
          key.channel().close();
         
         } else {
          // Update the index of "position" and "limit", change position to "0"; limit to "valid data length". 
          readBuffer.flip();
          int ValidDataLen = readBuffer.limit();
          final byte[] ReceiveData = new byte[ValidDataLen];
          readBuffer.get(ReceiveData);
          // Post read message to UI msg TextView. 
          runOnUiThread(new Runnable(){
          @Override
          public void run() {
          String msg = new String(ReceiveData); 
          mTvPostMsg.append("IP:"+ ClientSocketChannel.socket().getInetAddress().toString() + 
                   "(" + ClientSocketChannel.socket().getPort() + ")" + "say: " + msg + "\r\n");
          }});
         
          // Try to answer the data to client. 
          String answer = "Server say: your message: \""+ 
                    (new String(ReceiveData)) +
                    "\"has received.\r\n";
          ByteBuffer writeBuffer = ByteBuffer.wrap(answer.getBytes());
          
          int writeLength = ((SocketChannel) key.channel()).write(writeBuffer);;
          
          if (writeLength == 0) {// The channel buffer is full. 
          
           // Record the answer data. 
           mAnswerTemp = answer;
          
           // Register the OP_WRITE command. 
           key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
           
          }
         }
        } // else if (key.isReadable())
        else if (key.isWritable()) {
           
         ByteBuffer writeBuffer = ByteBuffer.wrap(mAnswerTemp.getBytes());
         ((SocketChannel) key.channel()).write(writeBuffer);
         
         mAnswerTemp = null;
         
         // Cancel the OP_WRITE command. 
         key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
           
        } // else if (key.isWritable())
          
       }// while (keyIter.hasNext() && (mSelector.isOpen() == true))
      }// while(isRunning == true)
     } catch (IOException e) {
      e.printStackTrace();
      Log.v(Class_TAG, "nioTCPSetverThread");
      Log.e(Debug_TAG, "The selecter selects fail.");
     }
    } catch (IOException e) {
     e.printStackTrace();
     Log.v(Class_TAG, "nioTCPSetverThread");
     Log.e(Debug_TAG, "Server socket channel \"open\" or \"bind\" fail.");
    }
   } catch (IOException e) {
    e.printStackTrace();
    Log.v(Class_TAG, "nioTCPSetverThread");
    Log.e(Debug_TAG, "Selector open fail.");
   }
   
   try {
    // Close server channel and selector. 
    mServerSocketChannel.keyFor(mSelector).cancel();
    mServerSocketChannel.socket().close();
    mServerSocketChannel.close();
    mServerSocketChannel = null;
    mSelector.close();
    mSelector = null;
    
    // Change the button state. 
    runOnUiThread(new Runnable(){
     @Override
     public void run() {
      mBtnBind.setEnabled(true);
      mTvHint.setText("Server close.");
    }});
    
   } catch (IOException e) {
    e.printStackTrace();
    Log.v(Class_TAG, "nioTCPSetverThread");
    Log.e(Debug_TAG, "Close server socket channel or selector fail.");
   }
  }
 };
 
 private String getLocalIP() {
     try {
      String ipv4;
      ArrayList<NetworkInterface>  nilist = Collections.list(NetworkInterface.getNetworkInterfaces());
      for (NetworkInterface ni: nilist)
      {
       ArrayList<InetAddress>  ialist = Collections.list(ni.getInetAddresses());
       for (InetAddress address: ialist){
        if (!address.isLoopbackAddress() && InetAddressUtils.isIPv4Address(ipv4=address.getHostAddress())) 
        { 
         return ipv4;
        }
       }
      }
     } catch (SocketException ex) {
      Log.v(Class_TAG, "getLocalIP");
    Log.e(Debug_TAG, "Error: get local IP address.");
     }
     return null;
 }
 
}

activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/xml_tv_hint"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:hint="Hint"
        android:textColor="#000000"
        android:textSize="30sp" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Port number:"
            android:textColor="#000000"
            android:textSize="20sp" />

        <EditText
            android:id="@+id/xml_et_port"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:hint="Enter the number 0~65535"
            android:textSize="20sp" >

            <requestFocus />
        </EditText>

        <Button
            android:id="@+id/xml_btn_bind"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0.54"
            android:text="Bind" />

        <Button
            android:id="@+id/xml_btn_close"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0.42"
            android:text="Close" />

    </LinearLayout>

    <TextView
        android:id="@+id/xml_tv_post_msg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="20dp" />

</LinearLayout>
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sample_niotcpserver"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.sample_niotcpserver.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>