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







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








Google Analytics