2014年6月4日 星期三

TLS Socket Sample

Uses Permission:
android.permission.INTERNET


MainActivity.java:
package com.example.sample_sslsocket;

import java.io.IOException;
import java.net.UnknownHostException;
import java.security.cert.Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

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



public class MainActivity extends Activity {
 private TextView textView;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  //get UI view
  textView = (TextView) this.findViewById(R.id.textView1);
  textView.setText("");
  //start SSL test sample.
  new Thread(new Runnable(){
   @Override
   public void run() {
    mainTest();
   }}).start();
  
 }
 private void TRACE(final String log){
  this.runOnUiThread(new Runnable(){
   @Override
   public void run() {
    Log.w("MainActivity", log);
    textView.append(log+"\r\n");   
   }}
  );
 }
 private void mainTest(){
  /**
  * 443 is the network port number used by the SSL https: URi scheme.
  */
  int port = 443;
  String hostname = "www.google.com";
  SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();

TRACE("Creating a SSL Socket For "+hostname+" on port "+port);
  
  SSLSocket socket = null;
  try {
   socket = (SSLSocket) factory.createSocket(hostname, port);
  } catch (UnknownHostException e) {
TRACE("factory.createSocket >> UnknownHostException");
  } catch (IOException e) {
TRACE("factory.createSocket >> IOException");
  }
TRACE("factory.createSocket >> successful");
  /**
  * Starts an SSL handshake on this connection. Common reasons include a
  * need to use new encryption keys, to change cipher suites, or to
  * initiate a new session. To force complete reauthentication, the
  * current session could be invalidated before starting this handshake.
  * If data has already been sent on the connection, it continues to flow
  * during this handshake. When the handshake completes, this will be
  * signaled with an event. This method is synchronous for the initial
  * handshake on a connection and returns when the negotiated handshake
  * is complete. Some protocols may not support multiple handshakes on an
  * existing socket and may throw an IOException.
  */
  try {
   socket.startHandshake();
  } catch (IOException e) {
TRACE("socket.startHandshake >> IOException");
  }
TRACE("Handshaking Complete");
  
  /**
  * Retrieve the server's certificate chain
  *
  * Returns the identity of the peer which was established as part of
  * defining the session. Note: This method can be used only when using
  * certificate-based cipher suites; using it with non-certificate-based
  * cipher suites, such as Kerberos, will throw an
  * SSLPeerUnverifiedException.
  *
  *
  * Returns: an ordered array of peer certificates, with the peer's own
  * certificate first followed by any certificate authorities.
  */
  Certificate[] serverCerts = null;
  try {
   serverCerts = socket.getSession().getPeerCertificates();
  } catch (SSLPeerUnverifiedException e) {
TRACE(" socket.getSession().getPeerCertificates >> SSLPeerUnverifiedException");
  }
TRACE("Retreived Server's Certificate Chain");
TRACE(serverCerts.length + "Certifcates Found\n\n\n");
  for (int i = 0; i < serverCerts.length; i++) {
   Certificate myCert = serverCerts[i];
TRACE("====Certificate:" + (i+1) + "====");
TRACE("-Public Key-\n" + myCert.getPublicKey());
TRACE("-Certificate Type-\n " + myCert.getType());
TRACE("");
  }
  
  
  String packet = "GET / HTTP/1.1\r\n\r\n";
TRACE("sending packet = "+packet);
  try {
   socket.getOutputStream().write(packet.getBytes());
TRACE("sending packet succeeded");
  } catch (IOException e1) {
TRACE("socket.getOutputStream().write >> IOException");
  }
TRACE("recving packet");
  byte[] recv = new byte[10000];
  try {
   int recvLen = socket.getInputStream().read(recv, 0, recv.length);
   String str = new String(recv, 0, recvLen);
TRACE("recv packet = "+str);
  } catch (IOException e1) {
TRACE("socket.getInputStream().read >> IOException");
  }
  
  
  try {
TRACE("closing socket");
   socket.close();
TRACE("socket closed");
  } catch (IOException e) {
TRACE("socket.close >> IOException");
  }
 }
}


activity_main.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.sample_sslsocket.MainActivity"
    tools:ignore="MergeRootFrame" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <ScrollView
            android:id="@+id/scrollView1"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" >

            <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:orientation="vertical" >

                <TextView
                    android:id="@+id/textView1"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    android:text="TextView" />

            </LinearLayout>
        </ScrollView>

    </LinearLayout>

</FrameLayout>

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

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />
    <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_sslsocket.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月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消失了~