2010年12月16日 星期四

[Android] 如何使用語音辨識功能 (Speech Recognition Method)



Android 是一個開放系統 , 所以可以在我們的應用程式中加入許多Google 的服務 , 例如這次的語音辨識服務 , 之前在這篇 : [Android] 如何使用Google Translate API 和 Spinner 製作翻譯機 也使用過翻譯服務 , 這個服務會使用到 RecognizerIntent , 而Google的語音搜尋程式通常都已經是先安裝在Android 的手機上了 , 可以透過 設定 > 應用程式 > 管理應用程式 做檢查 , 像遠傳小精靈IDEOS就安裝了TextToSpeech功能和語音搜尋功能。


Google為了讓語音辨識盡可能的準確 , 所以他分了很多語言模組 (Language Model) , 根據不同目的 , 不同的使用情況來使用 , 目前分了Free Form , 和 Web Search 兩個模組 , 前者適合長句子 , 後者適合短句子 , 而且是針對搜尋所會使用的詞彙去調整的 , 就端看程式人員認為該用在哪種情況囉。


Google的伺服器現在支援 , 英文 , 官方中文和日文 , 對於Free Form model 來說 , 英文版本有了最佳化 , 他們正開發更多的語言來支援這項服務 , 另外這個Method 模擬器沒辦法測試 , 下面的截圖是用IDEOS跑的。






請開啟網路 , 不然無法連線至伺服器


按下按鈕就等待使用者說話


辨識速度還蠻快的通常不到一秒


誤差很小喔


What......


講了幾個小數的運算 , 加減乘除沒出來



好玩 , 不過我是使用Free Form而最佳化目前只有英文 , 如果是用Web Search的話 , 中文的辨識度還蠻高的 , 雖然要連線但是我沒有註冊User-Permission , 程式碼就不貼啦 , 從這邊copy and paste就可以了 , Thanks Google!

2010年12月15日 星期三

[Smartphone] 遠傳小精靈 IDEOS



很感謝學長買了這隻遠傳小精靈 IDEOS給我當開發機 , 接下來要作的就是熟悉這個開發工具 , 在網路上查了他的評價不外乎就是....便宜很便宜 , 效能和配備中等 , 少了多點觸控是有點可惜 , 因為這個功能實在沒辦法靠模擬器測試出來 , 但是對照他的價錢來講已經是很不錯了 , 而且還附USB Driver光碟 , 這個Driver是可以讓我們開發的程式透過Eclipse呼叫手機直接來Debug的, 我當初還很擔心要去哪邊下載 , 以下是IDEOS的手機規格。




◎ HSDPA + GSM 四頻 850 / 900 / 1800 / 1900
◎ 直立式機身設計,3.5mm 標準耳機插孔
◎ 2.8 吋電容式觸控主螢幕、320 x 240 螢幕解析度、262000 萬色
可同時讓 5 個行動裝置連接上網
◎ 支援 Smart phone 服務
◎ 支援 FM 收音機
◎ 採用 Android 2.2 作業系統
◎ 內建重力感應器 / 光感感應器
◎ 內建 Qualcomm MSM7225-1 528MHz 處理器
◎ 320 萬畫素相機 (沒有補光燈 , 沒有自動對焦)
◎ 支援 3GP 影片錄製
◎ 支援 MID、MP3、M4A、AMR、AAC、AAC+、eAAC+ 音樂播放
◎ 支援 MPEG4、H.263、H.264 影片播放
◎ 支援 A2DP 藍牙 V2.1 傳輸
◎ 支援 GPS 衛星導航,Google Map
◎ 支援 WiFi 802.11b/g/n 無線網路

◎ 支援 HSDPA 7.2Mbps 網路連線速度
◎ 內建 512MB ROM / 256MB RAM 記憶體
◎ 可透過 microSD 記憶卡擴充 (最大可至 32GB)











再來附上一段宣傳短片







本來想拍幾張照片的 , 但是發現技術太差 , 拍得不好看的話 , 會不會被遠傳罵....哈

[Android] 如何使用 TextToSpeech (TTS)



這篇是要替上一篇 [Android] 如何使用Google Translate API 和 Spinner 製作翻譯機 所作的翻譯機加上發音功能 , 所以要使用到Android 的 android.speech.tts.TextToSpeech , 對於這個功能Android 官方的部落格有一篇文章介紹得很清楚: An introduction to Text-To-Speech in Android , 大家可以參考一下 , 有些Android 的手機由於儲存的容量限制 , 所以不一定有安裝 TextToSpeech Engine , 所以首先要先檢查TextToSpeech.Engine 是否有被安裝 , 如果沒安裝的話再使用Android API去安裝 , 另外不是每個語言都被支援發音 , 所以我們還要去check一下 , 系統是否支援 , Layout檔案和AndroidManifest.xml都和上一篇一樣沒更改 , 所以這邊只貼程式碼的部分。





package com.android;

import java.util.Locale;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Button;
import android.widget.Toast;

import com.google.api.translate.Language;
import com.google.api.translate.Translate;

public class GoogleTranslateSpinner extends Activity implements OnItemSelectedListener , Button.OnClickListener , OnInitListener
{
 /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        AAdapter = new ArrayAdapter(this,android.R.layout.simple_spinner_item,LanguageNameArray);
        AAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        //ArrayAdapter設定下拉式清單內容的layout格式
        //android. 開頭的範本是由Android提供的
        
        SpinnerFrom = (Spinner) findViewById(R.id.SpinnerFrom);
        SpinnerFrom.setAdapter(AAdapter);
        SpinnerFrom.setOnItemSelectedListener(this);
        SpinnerTo = (Spinner) findViewById(R.id.SpinnerTo);        
        SpinnerTo.setAdapter(AAdapter);
        SpinnerTo.setOnItemSelectedListener(this);        
        
        EditTextFrom = (EditText) findViewById(R.id.EditTextFrom);
        EditTextTo = (EditText) findViewById(R.id.EditTextTo);
        
        Button = (Button) findViewById(R.id.Button);
        Button.setOnClickListener(this);
        
        Translate.setHttpReferrer("http://shung007.blogspot.com/");
        //Translate.setHttpReferrer(" Enter the URL of your site here");
        //必要!!! 否則程式會出現Exception
        
        Intent checkIntent = new Intent();  
        checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);  
        startActivityForResult(checkIntent, MY_TTS_CHECK_CODE);  
        //前往檢查 TTS Engine有無安裝
    }
    
    final int MY_TTS_CHECK_CODE = 9527;
    TextToSpeech TToSpeech;
    
    Spinner SpinnerFrom , SpinnerTo;
    EditText EditTextFrom , EditTextTo;
    Button Button;
    
    int positionFrom , positionTo;
    
    ArrayAdapter AAdapter;
    
    public String[] LanguageNameArray = new String[]{
            "繁體中文", "CHINESE_SIMPLIFIED", "GERMAN", "ENGLISH",
            "JAPANESE", "FRENCH"
        };
    //作為顯示在Spinner裡的清單內容
    
    public Language[] LanguageCode = new Language[]{
        Language.CHINESE_TRADITIONAL, Language.CHINESE_SIMPLIFIED,
        Language.GERMAN, Language.ENGLISH, Language.JAPANESE, Language.FRENCH      
    };
    //相對應的LanguageCode , 作為之後傳遞的引數用    
    
    public Locale[] LocaleCode = new Locale[]{
      Locale.CHINESE, Locale.CHINA,
      Locale.GERMAN, Locale.ENGLISH, Locale.JAPANESE, Locale.FRENCH      
     };
    //相對應的Locale值 , 準備替我們所選擇的語言發音

    @Override  
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {  
      // TODO Auto-generated method stub  
      if(requestCode == MY_TTS_CHECK_CODE)
      {      
       if(resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) //如果TTS Engine有成功找到的話
          {         
          TToSpeech = new TextToSpeech(this, this);
            //宣告一個 TextToSpeech instance
         //並註冊android.speech.tts.TextToSpeech.OnInitListener
            //當TTS Engine 初始完畢之後會呼叫 onInit(int status)
          Log.d("onActivityResult" , "onInit");
          }
          else //如果TTS Engine 沒有安裝的話 , 要求API安裝  
          {            
             Intent installIntent = new Intent();  
             installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);  
             startActivity(installIntent);  
          }  
      }
    }
    
 @Override
 public void onItemSelected(AdapterView parent, View view, int position,
   long arg)
 {  
  if(parent.equals(SpinnerFrom)) positionFrom = position;
  else positionTo = position;
  //判斷並記錄使用者選擇了哪個語言翻譯
 }

 @Override
 public void onNothingSelected(AdapterView arg0)
 {
  // TODO Auto-generated method stub  
 }

 @Override
 public void onClick(View ButtonView)
 {
  //按下Translate按鈕之後 , 執行翻譯的動作
  try
  {
   EditTextTo.setText(Translate.execute(EditTextFrom.getText().toString(), LanguageCode[positionFrom], LanguageCode[positionTo]));
   
   int intLocal = TToSpeech.isLanguageAvailable(LocaleCode[positionFrom]);
   
   if(intLocal == TextToSpeech.LANG_MISSING_DATA || intLocal == TextToSpeech.LANG_NOT_SUPPORTED)
   {
    Toast.makeText(this, "不支援這個語言或是資料遺失", Toast.LENGTH_LONG).show();            
   }
   else
   {
    TToSpeech.setLanguage(LocaleCode[positionFrom]);
    TToSpeech.speak(EditTextFrom.getText().toString(), TextToSpeech.QUEUE_ADD, null);
      //發音 , 如果現在TTS Engine 正在發音的話 , 參數 TextToSpeech.QUEUE_FLUSH 代表會將其發音內容中斷 , 並且開始發音
      //TextToSpeech.QUEUE_ADD 代表會等到現在的發音的內容結束才開始發音      
   }
   
  } catch (Exception e)
  {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 @Override
 public void onInit(int status)
 {
  // TODO Auto-generated method stub
  Log.d("onInit" , "onInit");
 }
 
 @Override  
  protected void onDestroy()
 {  
     // TODO Auto-generated method stub  
     super.onDestroy();  
     TToSpeech.shutdown();  //釋放 TTS Engine所使用的資源
  }  
}


不過當按下Button的時候 , 要隔一兩秒才會發音 , 另外官網API Referece 的 Locale Class , 所提供的地區參數蠻多的 , 另外 TextToSpeech.OnInitListener interface的介紹 , 但是敘述的不多





看來不支援日文發音 , 就brother這個字來說 , 使用英文 , 德文和法文都可以成功發音


2010年12月7日 星期二

[Android] Android 2.3 Gingerbread Release




消息傳了那麼久終於在今天發佈了 , Android 2.3 Gingerbread 了 , 快去更新吧!!!!
這邊是AndroidUsersGuide-2.3 線上版pdf使用者手冊 , Android 2.3 Platform Hihglight 還針對使用者和開發者 , 新平台做了重點整理 ,  還有Demo的影片 , 如果不想看英文的話可以參考Andy Yang的這篇文章 他針對了幾個部分做了中文翻譯 , 可以搭配 Android 2.3 Platform Hihglight 文章內的圖片,  這樣會比較清楚 , copy/paste的功能終於改進了....ios都可以這樣使用很久了 , 還有些重點部分不知道可不可以在模擬器上測得出來....我開了GOOGLE API LEVEL 9 的模擬器 , 不過運作起來沒有很Smooth , 怪怪




裡面很多icon和介面都換過了 , 硬是要綠配黑....


另外對開發者來說很重要的部分是 , 更新了哪些API? 加入了哪些Package?
這邊有詳細的說明: Android API Differences Report , 希望之前遇到的Bug能藉由這次改版修復....

現在要使用到Android 2.3除非買 Google Nexus S , 而果真也是和之前傳聞的由三星代工


不過看起來還蠻有質感的 , 不知道等我當完兵會出到Nexus多少 哈


如果你使用Eclipse作為開發工具的話 , 可以參考這篇文章做更新(我是使用這個方法) , 成功之後Andorid SDK路徑的資料夾會多出你更新所下載的檔案 , 像我的Android_SDK\platforms\android-9 多出這個資料夾 , 我還看到Sumsung Galaxy Tab的元件可以下載...., 而這裡也可以直接下載Andorid 2.3 SDK Package




Andorid 2.3終於出了 , 不過我還在寫Android 2.2的case....



2010年12月5日 星期日

[Android] 如何發送簡訊並使用自訂的AlertDialog (How to send SMS and use custom AlertDialog)



就如標題說個該如何使用Android發送簡訊? 而對於使用自訂的AlertDialog , Android的Dev Guide有篇文章講解的很不錯 , 讓AlertDialog不再那麼單調了。


首先是程式碼的部分


package com.android;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class SMSAlertDialog extends Activity implements Button.OnClickListener
{
    /** Called when the activity is first created. */
    
 @Override
    public void onCreate(Bundle savedInstanceState)
 {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ButtonContact = (Button) findViewById(R.id.ButtonContact);
        ButtonContact.setOnClickListener(this);
        
        EditTextMS = (EditText) findViewById(R.id.EditTextMS);
  
    }
 
 AlertDialog alertDialog;
 Button ButtonContact , ButtonOk , ButtonCancel;
 EditText EditTextMS , EditTextNumber;

 @Override
 public void onClick(View ButtonView)
 {
  // TODO Auto-generated method stub
  
  switch(ButtonView.getId())
  {
      case R.id.ButtonContact:
      showAlertDialog();
         break;
         
         case R.id.ButtonOk:
         SmsManager smsManager = SmsManager.getDefault();
         //想獲得SmsManager物件必須呼叫static method SmsManager.getDefault().
         //API Level 4 開始使用 android.telephony.SmsManager裡的 SmsManager.getDefault();
            //API Level 1的android.telephony.gsm.SmsManager已不被推薦使用

      PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(), 0);
      //getBroadcast(Context context, int requestCode, Intent intent, int flags)
      //關於flags的說明可以參考官方的API Doc
      //http://developer.android.com/reference/android/app/PendingIntent.html
      smsManager.sendTextMessage(EditTextNumber.getText().toString(), null, EditTextMS.getText().toString(), pendingIntent, null);
      //傳送SMS
      Log.d(EditTextNumber.getText().toString(), EditTextMS.getText().toString());
      alertDialog.dismiss();      
      
      break;
         
         case R.id.ButtonCancel:      
            alertDialog.dismiss();            
      break;
  }
  
 }

 private void showAlertDialog()
 {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);   
    LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);

    final View layout = inflater.inflate(
     R.layout.customalertdialog, (LinearLayout) findViewById(R.id.layout_root));
    //將customalertdialog.xml中的 R.id.layout_root的LinearLayout並return 一個View
   
    ImageView ImageViewPhone = (ImageView) layout.findViewById(R.id.ImageViewPhone);
    //因為要取出LinearLayout裡的元素 , 所以 要使用layout來呼叫findViewById(int R.id) method
    //否則會出現NullPointerException    
    ImageViewPhone.setImageResource(R.drawable.phone);
    
    EditTextNumber = (EditText) layout.findViewById(R.id.EditTextNumber);
       
    ButtonOk = (Button) layout.findViewById(R.id.ButtonOk);
    ButtonOk.setOnClickListener(this);
    ButtonCancel = (Button) layout.findViewById(R.id.ButtonCancel);
    ButtonCancel.setOnClickListener(this);
    
    builder = new AlertDialog.Builder(this);   
    builder.setTitle("連絡人號碼");
    builder.setView(layout);
    
    alertDialog = builder.create();
    //當一切都設置完成之後才可以create()
    alertDialog.show();
    //顯示AlertDialog
 }
}


由於我把custom AlertDialog的Layout存在另一個檔案customalertdialog.xml , 以下是他的內容



















接下來是main.xml的內容










最後只要記得在AndroidManifest加上使用者權限就ok了







連絡人號碼我使用5556 , 是代表另一個模擬器的名稱 , 而我在IDEOS上測試過了也沒問題


這個5556模擬器已經收到來自剛剛5554模擬器發出的簡訊了


2010年12月3日 星期五

[Android] How to save primitive data by SharedPreferences


對於一個應用程式來說有時候會需要記錄使用者的一些設定 , 例如玩遊戲的分數記錄或是靜音是否開啟之類的訊息 , 在Android Developers的網頁有一篇關於Data Storage的文章 , 裡面了介紹了幾種關於儲存資料的方法 , 如果只是要儲存一些primitive data ex. boolean , string , long , int之類的話 , 可以使用SharedPreferences方便多了 , 而當使用者離開程式或是被迫中斷的時候 , 我們必須做儲存資料的動作 , 而等到使用者再執行程式時我們再把資料取出來 , 這邊可以參考 Activity Lifecycle , 來決定override哪些函式達到資料 存 取的目的。




首先是程式碼的部分 , 簡單的用來記錄一個CheckBox勾選的狀態


package com.android;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.CheckBox;

public class TestDec extends Activity
{
    
 /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        CB = (CheckBox) findViewById(R.id.CheckBox);
    }
    
    boolean CheckBoxStatus;
    CheckBox CB;
    final String PreferencesName = "urPreferencesName";
    
    @Override  
    protected void onStart()
    {
     super.onStart();
     
     SharedPreferences settings = getSharedPreferences(PreferencesName, MODE_PRIVATE);
        //透過Context類別下的 getSharedPreferences(String name, int mode) method 
     //取出 名為name 的 SharedPreferences , 如果不存在的話會幫你create一個
     //mode是存取的權限 , 預設為MODE_PRIVATE : 只有此應用程式可以存取
     
     CheckBoxStatus = settings.getBoolean("CheckBoxStatus", false);
        //從中取出名為CheckBoxStatus的布林值 , 如果不存在的話預設傳回false

        CB.setChecked(CheckBoxStatus);
    }
    
    @Override  
    protected void onStop()
    {
     super.onStop();
     
     SharedPreferences settings = getSharedPreferences(PreferencesName, MODE_PRIVATE);
        SharedPreferences.Editor editor = settings.edit();
        editor.putBoolean("CheckBoxStatus", CB.isChecked());
        //名稱同樣為 CheckBoxStatus
        
        editor.commit();
    }
}

這麼一來當使用者按back鍵離開之後也不會遺失資料了

2010年12月1日 星期三

[雜談] 曼秀雷敦 - Lip Ice 水果潤唇膏




最近在電視上看到這個30秒的廣告 , 被廣告的音樂所吸引了 , 進一步來說應該是被陳奕迅的歌聲所吸引了~~~廣告的輕鬆風格再配上Eason的歌聲真是令人感到舒服愉快 , 雖然我平常沒有買潤唇膏的習慣 , 但是每次看到這支廣告都會看完 , 這支廣告分為國語和粵語的版本 , 但由於Eason在廣告裡本來就是唱英文的所以差別在最後配音人員說的話。




台灣版本的廣告 - 有搭配中英字幕







粵語版本的廣告







拍攝的幕後花絮 - 看來是在香港拍的廣告 , 工作人員都是說粵語







這邊有官方網站的抽獎活動(至12 / 24日截止)








Eason so great!!!!



後來又找到了其他的版本 , 合唱版結尾還有張柏芝 0.0







這個又勾起我的回憶了 , 金城武以前也代言過








2010年11月28日 星期日

[NBA] NBA 2k11 CHEATS & CODES



Cheat : Codes

Enter the following codes by choosing "Features," > "Extras" > "Codes."

2ksports - Unlock the 2k Sports Team
2kchina - Unlock the 2k China Team
Payrespect - Unlock the ABA Ball
nba2k - Unlock the NBA2k Development Team
vcteam - Unlock the VC Squad
icanbe23 - MJ: Creating A Legend

2010年11月26日 星期五

[Android] 如何使用Google Translate API 和 Spinner 製作翻譯機



因為秉持Open Source的精神 , 所以Google 發佈了許多有趣的API給開發者拿來發揮創意 , 今天在網路上找到了Google Translate API 想先簡單的搭配 Spinner 做一個翻譯機出來試試 , 由於這個API不是存在Android 的jar檔內 , 所以我們必須先下載Google Translate 的jar檔 , 接著透過Eclipse就可以import jar檔到我們的程式當中了 , 首先到這裡去下載jar檔 , 接著在Eclipse中的 Package Explorer 中選取開發的專案 > Propertires > Java Build Path > 右邊的 Libabries > Add External JARs , 成功之後就可以發現多出一個圖示: Referenced Libabries , 裡面有剛剛加入的jar檔 , 接著試程式碼的部分




package com.android;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Button;

import com.google.api.translate.Language;
import com.google.api.translate.Translate;

public class GoogleTranslateSpinner extends Activity implements OnItemSelectedListener , Button.OnClickListener
{
 /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        AAdapter = new ArrayAdapter(this,android.R.layout.simple_spinner_item,LanguageNameArray);
        AAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        //ArrayAdapter設定下拉式清單內容的layout格式
        //android. 開頭的範本是由Android提供的
        
        SpinnerFrom = (Spinner) findViewById(R.id.SpinnerFrom);
        SpinnerFrom.setAdapter(AAdapter);
        SpinnerFrom.setOnItemSelectedListener(this);
        SpinnerTo = (Spinner) findViewById(R.id.SpinnerTo);        
        SpinnerTo.setAdapter(AAdapter);
        SpinnerTo.setOnItemSelectedListener(this);        
        
        EditTextFrom = (EditText) findViewById(R.id.EditTextFrom);
        EditTextTo = (EditText) findViewById(R.id.EditTextTo);
        
        Button = (Button) findViewById(R.id.Button);
        Button.setOnClickListener(this);
        
        Translate.setHttpReferrer("http://shung007.blogspot.com/");
        //Translate.setHttpReferrer(" Enter the URL of your site here");
        //必要!!! 否則程式會出現Exception
    }
    
    Spinner SpinnerFrom , SpinnerTo;
    EditText EditTextFrom , EditTextTo;
    Button Button;
    
    int positionFrom , positionTo;
    
    ArrayAdapter AAdapter;
    
    public String[] LanguageNameArray = new String[]{
            "繁體中文", "CHINESE_SIMPLIFIED", "GERMAN", "ENGLISH",
            "JAPANESE", "FRENCH"
        };
    //作為顯示在Spinner裡的清單內容
    
    public Language[] LanguageCode = new Language[]{
        Language.CHINESE_TRADITIONAL, Language.CHINESE_SIMPLIFIED,
        Language.GERMAN, Language.ENGLISH, Language.JAPANESE, Language.FRENCH      
    };
    //相對應的LanguageCode , 作為之後傳遞的引數用    

 @Override
 public void onItemSelected(AdapterView parent, View view, int position,
   long arg)
 {  
  if(parent.equals(SpinnerFrom)) positionFrom = position;
  else positionTo = position;
  //判斷並記錄使用者選擇了哪個語言翻譯
 }

 @Override
 public void onNothingSelected(AdapterView arg0)
 {
  // TODO Auto-generated method stub  
 }

 @Override
 public void onClick(View ButtonView)
 {
  //按下Translate按鈕之後 , 執行翻譯的動作
  try
  {
   EditTextTo.setText(Translate.execute(EditTextFrom.getText().toString(), LanguageCode[positionFrom], LanguageCode[positionTo]));
  } catch (Exception e)
  {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }    
}




值得一提的是他支持翻譯的語言相當多 , 不只我程式中列出的六個 , 從他的Language.java可以看得出來還支持哪些語言 , 另外 , 其實這個api需要透過網路把你輸入的內容傳送到資料庫進行翻譯 , 所以別忘了設定網路權限!!!!

<uses-permission android:name="android.permission.INTERNET"/>



最後是Layout的部分













 














整個程式很精簡但是很容易就達到我們的要求 , 因為最複雜的部分google都幫我們解決了 , 真好!




相關文章


[Android] 如何使用 TextToSpeech (TTS)

2010年11月24日 星期三

[Android] 人臉偵測 FaceDetector



今天在找資料的時候無意間發現這個Class : android.media.FaceDetector 覺得相當有趣 , 馬上放下手邊的事情尋找關於FaceDetector的資料 , 找了那麼多資料所以在這邊做個統整 , 首先這邊有關於FaceDetector的Source Code  , 想進一步研究的人可以去參考 , 不過關於API Doc的描述 , 進行偵測的圖片必須是RGB565的格式 , 再來是實作程式碼的部分




import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.media.FaceDetector;
import android.media.FaceDetector.Face;
import android.os.Bundle;
import android.view.View;

 
public class FaceDetector_Test extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(new FaceView(this));
    }  
    
    public static class FaceView extends View
    {
  int imageWidth, imageHeight;
        int numberOfFace = 10;
        //自訂能偵測出的臉孔數目
        FaceDetector myFaceDetect;
        FaceDetector.Face[] myFace;
        float myEyesDistance;
        int numberOfFaceDetected;
 
        Bitmap myBitmap;
        
        public FaceView(Context context)
        {
   super(context);
   // TODO Auto-generated constructor stub
   
   BitmapFactory.Options BFO = new BitmapFactory.Options();
            BFO.inPreferredConfig = Bitmap.Config.RGB_565;
            //由於FaceDetector只能處理RGB565格式的圖片 , 所以我們要對
            //圖片進行前處理的動作
            
            myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.play, BFO);
            //從getResources()中的物件找出在res/drawable裡的圖片
            //並以RGB565處理解譯成Bitmap物件
            
            imageWidth = myBitmap.getWidth();
            imageHeight = myBitmap.getHeight();            
            myFaceDetect = new FaceDetector(imageWidth, imageHeight, numberOfFace);
            //Note that the width of the image must be even.
            //Doc中有描述上面這句 , 不過不是很清楚用意
            
            myFace = new FaceDetector.Face[numberOfFace];
            //宣告一個FaceDetector.Face陣列 , 準備儲存所偵測到的臉孔位置資訊
            //所以陣列容量必定要和之前自訂的臉孔可偵測數目一樣
            
            numberOfFaceDetected = myFaceDetect.findFaces(myBitmap, myFace);
        }
        
        @Override
        protected void onDraw(Canvas canvas)
        {
            // TODO Auto-generated method stub
 
            canvas.drawBitmap(myBitmap, 0, 0, null);
 
            Paint myPaint = new Paint();
            myPaint.setColor(Color.GREEN);
            myPaint.setStyle(Paint.Style.STROKE);
            myPaint.setStrokeWidth(3);
 
            for(int i=0; i < numberOfFaceDetected; i++)
            {
             //將每個偵測出來的臉孔當畫上框框 
                Face face = myFace[i];
                PointF myMidPoint = new PointF();
                face.getMidPoint(myMidPoint);
                //兩眼中心點的point
                myEyesDistance = face.eyesDistance();
                //兩眼之間的距離
                canvas.drawRect(
                        (int)(myMidPoint.x - myEyesDistance),
                        (int)(myMidPoint.y - myEyesDistance),
                        (int)(myMidPoint.x + myEyesDistance),
                        (int)(myMidPoint.y + myEyesDistance),
                        myPaint);
                //如果用兩眼的距離能抓到臉長寬的比例
                //畫出的框框大概就可以框住臉了
            }            
        }
    }
}

 戴眼鏡的照片而且成功的結果

 失敗的結果....臉太紅? 眼睛太假?

最後把網路上一些開發者的建議做一個整理:
1. 其實看完程式碼大概可以能知道他是靠偵測眼睛來判斷人臉的位置 , 其他五官的細節就沒辦法偵測到了 , 所以雙眼必須同時出現在照片上才可以進行偵測,並且太陽眼鏡/眼鏡會影響檢測的效果。
2. 對於要偵測圖片的Pixels建議不要小於100x100 pixels否則會偵測不到 , 但是也不要太大 , 否則偵測得過程太久還有可能出現加載圖片例外的狀況 , 大概小於800x800 pixels效果不錯
3. 可以考慮開一個獨立的Thread來處理
4. 經過我的測試結果 , 如果說放了一張多人團體照的話 , 但是建構子中的 int maxFaces 參數設的很小例如"2" , 我猜測此時API會取他所分析出可能是人臉中 , Rank最高的前兩名作回報 , 所以把 maxFaces 調高一點可以讓團體照偵測的結果準很多 , 對於單人照其實沒太大差別。

下面是關於findFaces function的API Doc , 我特別把一段話標了起來

public int findFaces (Bitmap bitmap, Face[] faces)

Since: API Level 1
Finds all the faces found in a given Bitmap. The supplied array is populated with 
FaceDetector.Faces for each face found. The bitmap must be in 565 format (for now).
Parameters
bitmapthe Bitmap graphic to be analyzed
facesan array in which to place all found FaceDetector.Faces. The array must be sized equal to themaxFaces value set at initialization


2010年11月23日 星期二

[Android] 如何使用Google Map 2.0 & 3.0



接續之前的文章 [Android Tips] 如何取得手機的Cell ID 和 Location Area Code , 在我們獲得Cell ID 和 Location Area Code 之後 , 轉譯成我們要的地理座標呢? 這時候需要連上Google Map API接收他轉譯好的經緯度資訊 , 網址為: http://www.google.com/glm/mmap 點進去看到錯誤訊息不要以為他壞掉了 , 其實關於如何夠過上面的網址把Cell ID 和 Location Area Code轉譯成經緯度的資訊  Wei-Meng Lee poor man 的文章說得很清處 , 流程大概就是連接上網頁POST丟入參數然後解析網頁回傳的資訊。


在成功的獲得經緯度資訊後 , 就可以拿他們去呼叫Google Map定位了 , 在Google Map 3.0推出之後 , 官網呼籲開發者把2.0的版本改成3.0的 , 3.0 載入速度更快 , 尤其是在行動裝置的瀏覽器上(但是我用模擬器測試反應倒是有點慢....)


其實這次3.0的作法是希望把API放在伺服器上(有別於2.0的原生API) , 這樣開發者就不必因為Map版本得更心要手動去更新Code , 所以Google想要開發者直接把Google Map的網頁嵌入在程式內 , 所以!!! 到了3.0的版本 , 裝載Google Map的容器換成是WebView了(有別於2.0使用MapView) , 而他們的官方網頁有簡單的教學 。


接下來是2.0的部分 , 其實也不用介紹太多 , 很多網站和書籍都有範例 , 像Google 的Andorid 開發者網站就有這兩篇文章Google Map View 和 Location and Maps , Google Map API 2.0 是需要申請金鑰的 , 關於這個步驟這個網站說明的很詳細




相關文章:

Google Analytics